Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. | |
3 | * Copyright (c) 1988, 1989 by Adam de Boor | |
4 | * Copyright (c) 1989 by Berkeley Softworks | |
5 | * All rights reserved. | |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Adam de Boor. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
18 | * 3. All advertising materials mentioning features or use of this software | |
19 | * must display the following acknowledgement: | |
20 | * This product includes software developed by the University of | |
21 | * California, Berkeley and its contributors. | |
22 | * 4. Neither the name of the University nor the names of its contributors | |
23 | * may be used to endorse or promote products derived from this software | |
24 | * without specific prior written permission. | |
25 | * | |
26 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
27 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
30 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
31 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
32 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
34 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
35 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
36 | * SUCH DAMAGE. | |
37 | */ | |
38 | ||
39 | #ifndef lint | |
40 | static char sccsid[] = "@(#)arch.c 5.7 (Berkeley) 12/28/90"; | |
41 | #endif /* not lint */ | |
42 | ||
43 | /*- | |
44 | * arch.c -- | |
45 | * Functions to manipulate libraries, archives and their members. | |
46 | * | |
47 | * Once again, cacheing/hashing comes into play in the manipulation | |
48 | * of archives. The first time an archive is referenced, all of its members' | |
49 | * headers are read and hashed and the archive closed again. All hashed | |
50 | * archives are kept on a list which is searched each time an archive member | |
51 | * is referenced. | |
52 | * | |
53 | * The interface to this module is: | |
54 | * Arch_ParseArchive Given an archive specification, return a list | |
55 | * of GNode's, one for each member in the spec. | |
56 | * FAILURE is returned if the specification is | |
57 | * invalid for some reason. | |
58 | * | |
59 | * Arch_Touch Alter the modification time of the archive | |
60 | * member described by the given node to be | |
61 | * the current time. | |
62 | * | |
63 | * Arch_TouchLib Update the modification time of the library | |
64 | * described by the given node. This is special | |
65 | * because it also updates the modification time | |
66 | * of the library's table of contents. | |
67 | * | |
68 | * Arch_MTime Find the modification time of a member of | |
69 | * an archive *in the archive*. The time is also | |
70 | * placed in the member's GNode. Returns the | |
71 | * modification time. | |
72 | * | |
73 | * Arch_MemTime Find the modification time of a member of | |
74 | * an archive. Called when the member doesn't | |
75 | * already exist. Looks in the archive for the | |
76 | * modification time. Returns the modification | |
77 | * time. | |
78 | * | |
79 | * Arch_FindLib Search for a library along a path. The | |
80 | * library name in the GNode should be in | |
81 | * -l<name> format. | |
82 | * | |
83 | * Arch_LibOODate Special function to decide if a library node | |
84 | * is out-of-date. | |
85 | * | |
86 | * Arch_Init Initialize this module. | |
87 | */ | |
88 | ||
89 | #include <sys/types.h> | |
90 | #include <sys/stat.h> | |
91 | #include <sys/time.h> | |
92 | #include <ctype.h> | |
93 | #include <ar.h> | |
94 | #include <ranlib.h> | |
95 | #include <stdio.h> | |
96 | #include "make.h" | |
97 | #include "hash.h" | |
98 | ||
99 | static Lst archives; /* Lst of archives we've already examined */ | |
100 | ||
101 | typedef struct Arch { | |
102 | char *name; /* Name of archive */ | |
103 | Hash_Table members; /* All the members of the archive described | |
104 | * by <name, struct ar_hdr *> key/value pairs */ | |
105 | } Arch; | |
106 | ||
107 | static FILE *ArchFindMember(); | |
108 | ||
109 | /*- | |
110 | *----------------------------------------------------------------------- | |
111 | * Arch_ParseArchive -- | |
112 | * Parse the archive specification in the given line and find/create | |
113 | * the nodes for the specified archive members, placing their nodes | |
114 | * on the given list. | |
115 | * | |
116 | * Results: | |
117 | * SUCCESS if it was a valid specification. The linePtr is updated | |
118 | * to point to the first non-space after the archive spec. The | |
119 | * nodes for the members are placed on the given list. | |
120 | * | |
121 | * Side Effects: | |
122 | * Some nodes may be created. The given list is extended. | |
123 | * | |
124 | *----------------------------------------------------------------------- | |
125 | */ | |
126 | ReturnStatus | |
127 | Arch_ParseArchive (linePtr, nodeLst, ctxt) | |
128 | char **linePtr; /* Pointer to start of specification */ | |
129 | Lst nodeLst; /* Lst on which to place the nodes */ | |
130 | GNode *ctxt; /* Context in which to expand variables */ | |
131 | { | |
132 | register char *cp; /* Pointer into line */ | |
133 | GNode *gn; /* New node */ | |
134 | char *libName; /* Library-part of specification */ | |
135 | char *memName; /* Member-part of specification */ | |
136 | char nameBuf[BSIZE]; /* temporary place for node name */ | |
137 | char saveChar; /* Ending delimiter of member-name */ | |
138 | Boolean subLibName; /* TRUE if libName should have/had | |
139 | * variable substitution performed on it */ | |
140 | ||
141 | libName = *linePtr; | |
142 | ||
143 | subLibName = FALSE; | |
144 | ||
145 | for (cp = libName; *cp != '(' && *cp != '\0'; cp++) { | |
146 | if (*cp == '$') { | |
147 | /* | |
148 | * Variable spec, so call the Var module to parse the puppy | |
149 | * so we can safely advance beyond it... | |
150 | */ | |
151 | int length; | |
152 | Boolean freeIt; | |
153 | char *result; | |
154 | ||
155 | result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); | |
156 | if (result == var_Error) { | |
157 | return(FAILURE); | |
158 | } else { | |
159 | subLibName = TRUE; | |
160 | } | |
161 | ||
162 | if (freeIt) { | |
163 | free(result); | |
164 | } | |
165 | cp += length-1; | |
166 | } | |
167 | } | |
168 | ||
169 | *cp++ = '\0'; | |
170 | if (subLibName) { | |
171 | libName = Var_Subst(libName, ctxt, TRUE); | |
172 | } | |
173 | ||
174 | ||
175 | while (1) { | |
176 | /* | |
177 | * First skip to the start of the member's name, mark that | |
178 | * place and skip to the end of it (either white-space or | |
179 | * a close paren). | |
180 | */ | |
181 | Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ | |
182 | ||
183 | while (*cp != '\0' && *cp != ')' && isspace (*cp)) { | |
184 | cp++; | |
185 | } | |
186 | memName = cp; | |
187 | while (*cp != '\0' && *cp != ')' && !isspace (*cp)) { | |
188 | if (*cp == '$') { | |
189 | /* | |
190 | * Variable spec, so call the Var module to parse the puppy | |
191 | * so we can safely advance beyond it... | |
192 | */ | |
193 | int length; | |
194 | Boolean freeIt; | |
195 | char *result; | |
196 | ||
197 | result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); | |
198 | if (result == var_Error) { | |
199 | return(FAILURE); | |
200 | } else { | |
201 | doSubst = TRUE; | |
202 | } | |
203 | ||
204 | if (freeIt) { | |
205 | free(result); | |
206 | } | |
207 | cp += length; | |
208 | } else { | |
209 | cp++; | |
210 | } | |
211 | } | |
212 | ||
213 | /* | |
214 | * If the specification ends without a closing parenthesis, | |
215 | * chances are there's something wrong (like a missing backslash), | |
216 | * so it's better to return failure than allow such things to happen | |
217 | */ | |
218 | if (*cp == '\0') { | |
219 | printf("No closing parenthesis in archive specification\n"); | |
220 | return (FAILURE); | |
221 | } | |
222 | ||
223 | /* | |
224 | * If we didn't move anywhere, we must be done | |
225 | */ | |
226 | if (cp == memName) { | |
227 | break; | |
228 | } | |
229 | ||
230 | saveChar = *cp; | |
231 | *cp = '\0'; | |
232 | ||
233 | /* | |
234 | * XXX: This should be taken care of intelligently by | |
235 | * SuffExpandChildren, both for the archive and the member portions. | |
236 | */ | |
237 | /* | |
238 | * If member contains variables, try and substitute for them. | |
239 | * This will slow down archive specs with dynamic sources, of course, | |
240 | * since we'll be (non-)substituting them three times, but them's | |
241 | * the breaks -- we need to do this since SuffExpandChildren calls | |
242 | * us, otherwise we could assume the thing would be taken care of | |
243 | * later. | |
244 | */ | |
245 | if (doSubst) { | |
246 | char *buf; | |
247 | char *sacrifice; | |
248 | char *oldMemName = memName; | |
249 | ||
250 | memName = Var_Subst(memName, ctxt, TRUE); | |
251 | ||
252 | /* | |
253 | * Now form an archive spec and recurse to deal with nested | |
254 | * variables and multi-word variable values.... The results | |
255 | * are just placed at the end of the nodeLst we're returning. | |
256 | */ | |
257 | buf = sacrifice = emalloc(strlen(memName)+strlen(libName)+3); | |
258 | ||
259 | sprintf(buf, "%s(%s)", libName, memName); | |
260 | ||
261 | if (index(memName, '$') && strcmp(memName, oldMemName) == 0) { | |
262 | /* | |
263 | * Must contain dynamic sources, so we can't deal with it now. | |
264 | * Just create an ARCHV node for the thing and let | |
265 | * SuffExpandChildren handle it... | |
266 | */ | |
267 | gn = Targ_FindNode(buf, TARG_CREATE); | |
268 | ||
269 | if (gn == NILGNODE) { | |
270 | free(buf); | |
271 | return(FAILURE); | |
272 | } else { | |
273 | gn->type |= OP_ARCHV; | |
274 | (void)Lst_AtEnd(nodeLst, (ClientData)gn); | |
275 | } | |
276 | } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { | |
277 | /* | |
278 | * Error in nested call -- free buffer and return FAILURE | |
279 | * ourselves. | |
280 | */ | |
281 | free(buf); | |
282 | return(FAILURE); | |
283 | } | |
284 | /* | |
285 | * Free buffer and continue with our work. | |
286 | */ | |
287 | free(buf); | |
288 | } else if (Dir_HasWildcards(memName)) { | |
289 | Lst members = Lst_Init(FALSE); | |
290 | char *member; | |
291 | ||
292 | Dir_Expand(memName, dirSearchPath, members); | |
293 | while (!Lst_IsEmpty(members)) { | |
294 | member = (char *)Lst_DeQueue(members); | |
295 | ||
296 | sprintf(nameBuf, "%s(%s)", libName, member); | |
297 | free(member); | |
298 | gn = Targ_FindNode (nameBuf, TARG_CREATE); | |
299 | if (gn == NILGNODE) { | |
300 | return (FAILURE); | |
301 | } else { | |
302 | /* | |
303 | * We've found the node, but have to make sure the rest of | |
304 | * the world knows it's an archive member, without having | |
305 | * to constantly check for parentheses, so we type the | |
306 | * thing with the OP_ARCHV bit before we place it on the | |
307 | * end of the provided list. | |
308 | */ | |
309 | gn->type |= OP_ARCHV; | |
310 | (void) Lst_AtEnd (nodeLst, (ClientData)gn); | |
311 | } | |
312 | } | |
313 | Lst_Destroy(members, NOFREE); | |
314 | } else { | |
315 | sprintf(nameBuf, "%s(%s)", libName, memName); | |
316 | gn = Targ_FindNode (nameBuf, TARG_CREATE); | |
317 | if (gn == NILGNODE) { | |
318 | return (FAILURE); | |
319 | } else { | |
320 | /* | |
321 | * We've found the node, but have to make sure the rest of the | |
322 | * world knows it's an archive member, without having to | |
323 | * constantly check for parentheses, so we type the thing with | |
324 | * the OP_ARCHV bit before we place it on the end of the | |
325 | * provided list. | |
326 | */ | |
327 | gn->type |= OP_ARCHV; | |
328 | (void) Lst_AtEnd (nodeLst, (ClientData)gn); | |
329 | } | |
330 | } | |
331 | if (doSubst) { | |
332 | free(memName); | |
333 | } | |
334 | ||
335 | *cp = saveChar; | |
336 | } | |
337 | ||
338 | /* | |
339 | * If substituted libName, free it now, since we need it no longer. | |
340 | */ | |
341 | if (subLibName) { | |
342 | free(libName); | |
343 | } | |
344 | ||
345 | /* | |
346 | * We promised the pointer would be set up at the next non-space, so | |
347 | * we must advance cp there before setting *linePtr... (note that on | |
348 | * entrance to the loop, cp is guaranteed to point at a ')') | |
349 | */ | |
350 | do { | |
351 | cp++; | |
352 | } while (*cp != '\0' && isspace (*cp)); | |
353 | ||
354 | *linePtr = cp; | |
355 | return (SUCCESS); | |
356 | } | |
357 | ||
358 | /*- | |
359 | *----------------------------------------------------------------------- | |
360 | * ArchFindArchive -- | |
361 | * See if the given archive is the one we are looking for. Called | |
362 | * From ArchStatMember and ArchFindMember via Lst_Find. | |
363 | * | |
364 | * Results: | |
365 | * 0 if it is, non-zero if it isn't. | |
366 | * | |
367 | * Side Effects: | |
368 | * None. | |
369 | * | |
370 | *----------------------------------------------------------------------- | |
371 | */ | |
372 | static int | |
373 | ArchFindArchive (ar, archName) | |
374 | Arch *ar; /* Current list element */ | |
375 | char *archName; /* Name we want */ | |
376 | { | |
377 | return (strcmp (archName, ar->name)); | |
378 | } | |
379 | ||
380 | /*- | |
381 | *----------------------------------------------------------------------- | |
382 | * ArchStatMember -- | |
383 | * Locate a member of an archive, given the path of the archive and | |
384 | * the path of the desired member. | |
385 | * | |
386 | * Results: | |
387 | * A pointer to the current struct ar_hdr structure for the member. Note | |
388 | * That no position is returned, so this is not useful for touching | |
389 | * archive members. This is mostly because we have no assurances that | |
390 | * The archive will remain constant after we read all the headers, so | |
391 | * there's not much point in remembering the position... | |
392 | * | |
393 | * Side Effects: | |
394 | * | |
395 | *----------------------------------------------------------------------- | |
396 | */ | |
397 | static struct ar_hdr * | |
398 | ArchStatMember (archive, member, hash) | |
399 | char *archive; /* Path to the archive */ | |
400 | char *member; /* Name of member. If it is a path, only the | |
401 | * last component is used. */ | |
402 | Boolean hash; /* TRUE if archive should be hashed if not | |
403 | * already so. */ | |
404 | { | |
405 | #define AR_MAX_NAME_LEN (sizeof(arh.ar_name)-1) | |
406 | FILE * arch; /* Stream to archive */ | |
407 | int size; /* Size of archive member */ | |
408 | char *cp; /* Useful character pointer */ | |
409 | char magic[SARMAG]; | |
410 | int len; | |
411 | LstNode ln; /* Lst member containing archive descriptor */ | |
412 | Arch *ar; /* Archive descriptor */ | |
413 | Hash_Entry *he; /* Entry containing member's description */ | |
414 | struct ar_hdr arh; /* archive-member header for reading archive */ | |
415 | char memName[AR_MAX_NAME_LEN+1]; | |
416 | /* Current member name while hashing. The name is | |
417 | * truncated to AR_MAX_NAME_LEN bytes, but we need | |
418 | * room for the null byte... */ | |
419 | char copy[AR_MAX_NAME_LEN+1]; | |
420 | /* Holds copy of last path element from member, if | |
421 | * it has to be truncated, so we don't have to | |
422 | * figure it out again once the table is hashed. */ | |
423 | ||
424 | /* | |
425 | * Because of space constraints and similar things, files are archived | |
426 | * using their final path components, not the entire thing, so we need | |
427 | * to point 'member' to the final component, if there is one, to make | |
428 | * the comparisons easier... | |
429 | */ | |
430 | cp = rindex (member, '/'); | |
431 | if (cp != (char *) NULL) { | |
432 | member = cp + 1; | |
433 | } | |
434 | len = strlen (member); | |
435 | if (len > AR_MAX_NAME_LEN) { | |
436 | len = AR_MAX_NAME_LEN; | |
437 | strncpy(copy, member, AR_MAX_NAME_LEN); | |
438 | copy[AR_MAX_NAME_LEN] = '\0'; | |
439 | member = copy; | |
440 | } | |
441 | ||
442 | ln = Lst_Find (archives, (ClientData) archive, ArchFindArchive); | |
443 | if (ln != NILLNODE) { | |
444 | ar = (Arch *) Lst_Datum (ln); | |
445 | ||
446 | he = Hash_FindEntry (&ar->members, member); | |
447 | ||
448 | if (he != (Hash_Entry *) NULL) { | |
449 | return ((struct ar_hdr *) Hash_GetValue (he)); | |
450 | } else { | |
451 | return ((struct ar_hdr *) NULL); | |
452 | } | |
453 | } | |
454 | ||
455 | if (!hash) { | |
456 | /* | |
457 | * Caller doesn't want the thing hashed, just use ArchFindMember | |
458 | * to read the header for the member out and close down the stream | |
459 | * again. Since the archive is not to be hashed, we assume there's | |
460 | * no need to allocate extra room for the header we're returning, | |
461 | * so just declare it static. | |
462 | */ | |
463 | static struct ar_hdr sarh; | |
464 | ||
465 | arch = ArchFindMember(archive, member, &sarh, "r"); | |
466 | ||
467 | if (arch == (FILE *)NULL) { | |
468 | return ((struct ar_hdr *)NULL); | |
469 | } else { | |
470 | fclose(arch); | |
471 | return (&sarh); | |
472 | } | |
473 | } | |
474 | ||
475 | /* | |
476 | * We don't have this archive on the list yet, so we want to find out | |
477 | * everything that's in it and cache it so we can get at it quickly. | |
478 | */ | |
479 | arch = fopen (archive, "r"); | |
480 | if (arch == (FILE *) NULL) { | |
481 | return ((struct ar_hdr *) NULL); | |
482 | } | |
483 | ||
484 | /* | |
485 | * We use the ARMAG string to make sure this is an archive we | |
486 | * can handle... | |
487 | */ | |
488 | if ((fread (magic, SARMAG, 1, arch) != 1) || | |
489 | (strncmp (magic, ARMAG, SARMAG) != 0)) { | |
490 | fclose (arch); | |
491 | return ((struct ar_hdr *) NULL); | |
492 | } | |
493 | ||
494 | ar = (Arch *)emalloc (sizeof (Arch)); | |
495 | ar->name = strdup (archive); | |
496 | Hash_InitTable (&ar->members, -1); | |
497 | memName[AR_MAX_NAME_LEN] = '\0'; | |
498 | ||
499 | while (fread ((char *)&arh, sizeof (struct ar_hdr), 1, arch) == 1) { | |
500 | if (strncmp ( arh.ar_fmag, ARFMAG, sizeof (arh.ar_fmag)) != 0) { | |
501 | /* | |
502 | * The header is bogus, so the archive is bad | |
503 | * and there's no way we can recover... | |
504 | */ | |
505 | fclose (arch); | |
506 | Hash_DeleteTable (&ar->members); | |
507 | free ((Address)ar); | |
508 | return ((struct ar_hdr *) NULL); | |
509 | } else { | |
510 | (void) strncpy (memName, arh.ar_name, sizeof(arh.ar_name)); | |
511 | for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { | |
512 | continue; | |
513 | } | |
514 | cp[1] = '\0'; | |
515 | ||
516 | he = Hash_CreateEntry (&ar->members, strdup (memName), | |
517 | (Boolean *)NULL); | |
518 | Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr))); | |
519 | bcopy ((Address)&arh, (Address)Hash_GetValue (he), | |
520 | sizeof (struct ar_hdr)); | |
521 | } | |
522 | /* | |
523 | * We need to advance the stream's pointer to the start of the | |
524 | * next header. Files are padded with newlines to an even-byte | |
525 | * boundary, so we need to extract the size of the file from the | |
526 | * 'size' field of the header and round it up during the seek. | |
527 | */ | |
528 | arh.ar_size[sizeof(arh.ar_size)-1] = '\0'; | |
529 | (void) sscanf (arh.ar_size, "%10d", &size); | |
530 | fseek (arch, (size + 1) & ~1, 1); | |
531 | } | |
532 | ||
533 | fclose (arch); | |
534 | ||
535 | (void) Lst_AtEnd (archives, (ClientData) ar); | |
536 | ||
537 | /* | |
538 | * Now that the archive has been read and cached, we can look into | |
539 | * the hash table to find the desired member's header. | |
540 | */ | |
541 | he = Hash_FindEntry (&ar->members, member); | |
542 | ||
543 | if (he != (Hash_Entry *) NULL) { | |
544 | return ((struct ar_hdr *) Hash_GetValue (he)); | |
545 | } else { | |
546 | return ((struct ar_hdr *) NULL); | |
547 | } | |
548 | } | |
549 | ||
550 | /*- | |
551 | *----------------------------------------------------------------------- | |
552 | * ArchFindMember -- | |
553 | * Locate a member of an archive, given the path of the archive and | |
554 | * the path of the desired member. If the archive is to be modified, | |
555 | * the mode should be "r+", if not, it should be "r". | |
556 | * | |
557 | * Results: | |
558 | * An FILE *, opened for reading and writing, positioned at the | |
559 | * start of the member's struct ar_hdr, or NULL if the member was | |
560 | * nonexistent. The current struct ar_hdr for member. | |
561 | * | |
562 | * Side Effects: | |
563 | * The passed struct ar_hdr structure is filled in. | |
564 | * | |
565 | *----------------------------------------------------------------------- | |
566 | */ | |
567 | static FILE * | |
568 | ArchFindMember (archive, member, arhPtr, mode) | |
569 | char *archive; /* Path to the archive */ | |
570 | char *member; /* Name of member. If it is a path, only the | |
571 | * last component is used. */ | |
572 | struct ar_hdr *arhPtr; /* Pointer to header structure to be filled in */ | |
573 | char *mode; /* The mode for opening the stream */ | |
574 | { | |
575 | FILE * arch; /* Stream to archive */ | |
576 | int size; /* Size of archive member */ | |
577 | char *cp; /* Useful character pointer */ | |
578 | char magic[SARMAG]; | |
579 | int len; | |
580 | ||
581 | arch = fopen (archive, mode); | |
582 | if (arch == (FILE *) NULL) { | |
583 | return ((FILE *) NULL); | |
584 | } | |
585 | ||
586 | /* | |
587 | * We use the ARMAG string to make sure this is an archive we | |
588 | * can handle... | |
589 | */ | |
590 | if ((fread (magic, SARMAG, 1, arch) != 1) || | |
591 | (strncmp (magic, ARMAG, SARMAG) != 0)) { | |
592 | fclose (arch); | |
593 | return ((FILE *) NULL); | |
594 | } | |
595 | ||
596 | /* | |
597 | * Because of space constraints and similar things, files are archived | |
598 | * using their final path components, not the entire thing, so we need | |
599 | * to point 'member' to the final component, if there is one, to make | |
600 | * the comparisons easier... | |
601 | */ | |
602 | cp = rindex (member, '/'); | |
603 | if (cp != (char *) NULL) { | |
604 | member = cp + 1; | |
605 | } | |
606 | len = strlen (member); | |
607 | if (len > sizeof (arhPtr->ar_name)) { | |
608 | len = sizeof (arhPtr->ar_name); | |
609 | } | |
610 | ||
611 | while (fread ((char *)arhPtr, sizeof (struct ar_hdr), 1, arch) == 1) { | |
612 | if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof (arhPtr->ar_fmag) ) != 0) { | |
613 | /* | |
614 | * The header is bogus, so the archive is bad | |
615 | * and there's no way we can recover... | |
616 | */ | |
617 | fclose (arch); | |
618 | return ((FILE *) NULL); | |
619 | } else if (strncmp (member, arhPtr->ar_name, len) == 0) { | |
620 | /* | |
621 | * If the member's name doesn't take up the entire 'name' field, | |
622 | * we have to be careful of matching prefixes. Names are space- | |
623 | * padded to the right, so if the character in 'name' at the end | |
624 | * of the matched string is anything but a space, this isn't the | |
625 | * member we sought. | |
626 | */ | |
627 | if (len != sizeof(arhPtr->ar_name) && arhPtr->ar_name[len] != ' '){ | |
628 | continue; | |
629 | } else { | |
630 | /* | |
631 | * To make life easier, we reposition the file at the start | |
632 | * of the header we just read before we return the stream. | |
633 | * In a more general situation, it might be better to leave | |
634 | * the file at the actual member, rather than its header, but | |
635 | * not here... | |
636 | */ | |
637 | fseek (arch, -sizeof(struct ar_hdr), 1); | |
638 | return (arch); | |
639 | } | |
640 | } else { | |
641 | /* | |
642 | * This isn't the member we're after, so we need to advance the | |
643 | * stream's pointer to the start of the next header. Files are | |
644 | * padded with newlines to an even-byte boundary, so we need to | |
645 | * extract the size of the file from the 'size' field of the | |
646 | * header and round it up during the seek. | |
647 | */ | |
648 | arhPtr->ar_size[sizeof(arhPtr->ar_size)-1] = '\0'; | |
649 | (void)sscanf (arhPtr->ar_size, "%10d", &size); | |
650 | fseek (arch, (size + 1) & ~1, 1); | |
651 | } | |
652 | } | |
653 | ||
654 | /* | |
655 | * We've looked everywhere, but the member is not to be found. Close the | |
656 | * archive and return NULL -- an error. | |
657 | */ | |
658 | fclose (arch); | |
659 | return ((FILE *) NULL); | |
660 | } | |
661 | ||
662 | /*- | |
663 | *----------------------------------------------------------------------- | |
664 | * Arch_Touch -- | |
665 | * Touch a member of an archive. | |
666 | * | |
667 | * Results: | |
668 | * The 'time' field of the member's header is updated. | |
669 | * | |
670 | * Side Effects: | |
671 | * The modification time of the entire archive is also changed. | |
672 | * For a library, this could necessitate the re-ranlib'ing of the | |
673 | * whole thing. | |
674 | * | |
675 | *----------------------------------------------------------------------- | |
676 | */ | |
677 | void | |
678 | Arch_Touch (gn) | |
679 | GNode *gn; /* Node of member to touch */ | |
680 | { | |
681 | FILE * arch; /* Stream open to archive, positioned properly */ | |
682 | struct ar_hdr arh; /* Current header describing member */ | |
683 | ||
684 | arch = ArchFindMember(Var_Value (ARCHIVE, gn), | |
685 | Var_Value (TARGET, gn), | |
686 | &arh, "r+"); | |
687 | sprintf(arh.ar_date, "%-12d", now); | |
688 | ||
689 | if (arch != (FILE *) NULL) { | |
690 | (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); | |
691 | fclose (arch); | |
692 | } | |
693 | } | |
694 | ||
695 | /*- | |
696 | *----------------------------------------------------------------------- | |
697 | * Arch_TouchLib -- | |
698 | * Given a node which represents a library, touch the thing, making | |
699 | * sure that the table of contents also is touched. | |
700 | * | |
701 | * Results: | |
702 | * None. | |
703 | * | |
704 | * Side Effects: | |
705 | * Both the modification time of the library and of the RANLIBMAG | |
706 | * member are set to 'now'. | |
707 | * | |
708 | *----------------------------------------------------------------------- | |
709 | */ | |
710 | void | |
711 | Arch_TouchLib (gn) | |
712 | GNode *gn; /* The node of the library to touch */ | |
713 | { | |
714 | FILE * arch; /* Stream open to archive */ | |
715 | struct ar_hdr arh; /* Header describing table of contents */ | |
716 | struct timeval times[2]; /* Times for utimes() call */ | |
717 | ||
718 | arch = ArchFindMember (gn->path, RANLIBMAG, &arh, "r+"); | |
719 | sprintf(arh.ar_date, "%-12d", now); | |
720 | ||
721 | if (arch != (FILE *) NULL) { | |
722 | (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); | |
723 | fclose (arch); | |
724 | ||
725 | times[0].tv_sec = times[1].tv_sec = now; | |
726 | times[0].tv_usec = times[1].tv_usec = 0; | |
727 | utimes(gn->path, times); | |
728 | } | |
729 | } | |
730 | ||
731 | /*- | |
732 | *----------------------------------------------------------------------- | |
733 | * Arch_MTime -- | |
734 | * Return the modification time of a member of an archive. | |
735 | * | |
736 | * Results: | |
737 | * The modification time (seconds). | |
738 | * | |
739 | * Side Effects: | |
740 | * The mtime field of the given node is filled in with the value | |
741 | * returned by the function. | |
742 | * | |
743 | *----------------------------------------------------------------------- | |
744 | */ | |
745 | int | |
746 | Arch_MTime (gn) | |
747 | GNode *gn; /* Node describing archive member */ | |
748 | { | |
749 | struct ar_hdr *arhPtr; /* Header of desired member */ | |
750 | int modTime; /* Modification time as an integer */ | |
751 | ||
752 | arhPtr = ArchStatMember (Var_Value (ARCHIVE, gn), | |
753 | Var_Value (TARGET, gn), | |
754 | TRUE); | |
755 | if (arhPtr != (struct ar_hdr *) NULL) { | |
756 | (void)sscanf (arhPtr->ar_date, "%12d", &modTime); | |
757 | } else { | |
758 | modTime = 0; | |
759 | } | |
760 | ||
761 | gn->mtime = modTime; | |
762 | return (modTime); | |
763 | } | |
764 | ||
765 | /*- | |
766 | *----------------------------------------------------------------------- | |
767 | * Arch_MemMTime -- | |
768 | * Given a non-existent archive member's node, get its modification | |
769 | * time from its archived form, if it exists. | |
770 | * | |
771 | * Results: | |
772 | * The modification time. | |
773 | * | |
774 | * Side Effects: | |
775 | * The mtime field is filled in. | |
776 | * | |
777 | *----------------------------------------------------------------------- | |
778 | */ | |
779 | int | |
780 | Arch_MemMTime (gn) | |
781 | GNode *gn; | |
782 | { | |
783 | LstNode ln; | |
784 | GNode *pgn; | |
785 | char *nameStart, | |
786 | *nameEnd; | |
787 | ||
788 | if (Lst_Open (gn->parents) != SUCCESS) { | |
789 | gn->mtime = 0; | |
790 | return (0); | |
791 | } | |
792 | while ((ln = Lst_Next (gn->parents)) != NILLNODE) { | |
793 | pgn = (GNode *) Lst_Datum (ln); | |
794 | ||
795 | if (pgn->type & OP_ARCHV) { | |
796 | /* | |
797 | * If the parent is an archive specification and is being made | |
798 | * and its member's name matches the name of the node we were | |
799 | * given, record the modification time of the parent in the | |
800 | * child. We keep searching its parents in case some other | |
801 | * parent requires this child to exist... | |
802 | */ | |
803 | nameStart = index (pgn->name, '(') + 1; | |
804 | nameEnd = index (nameStart, ')'); | |
805 | ||
806 | if (pgn->make && | |
807 | strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { | |
808 | gn->mtime = Arch_MTime(pgn); | |
809 | } | |
810 | } else if (pgn->make) { | |
811 | /* | |
812 | * Something which isn't a library depends on the existence of | |
813 | * this target, so it needs to exist. | |
814 | */ | |
815 | gn->mtime = 0; | |
816 | break; | |
817 | } | |
818 | } | |
819 | ||
820 | Lst_Close (gn->parents); | |
821 | ||
822 | return (gn->mtime); | |
823 | } | |
824 | ||
825 | /*- | |
826 | *----------------------------------------------------------------------- | |
827 | * Arch_FindLib -- | |
828 | * Search for a library along the given search path. | |
829 | * | |
830 | * Results: | |
831 | * None. | |
832 | * | |
833 | * Side Effects: | |
834 | * The node's 'path' field is set to the found path (including the | |
835 | * actual file name, not -l...). If the system can handle the -L | |
836 | * flag when linking (or we cannot find the library), we assume that | |
837 | * the user has placed the .LIBRARIES variable in the final linking | |
838 | * command (or the linker will know where to find it) and set the | |
839 | * TARGET variable for this node to be the node's name. Otherwise, | |
840 | * we set the TARGET variable to be the full path of the library, | |
841 | * as returned by Dir_FindFile. | |
842 | * | |
843 | *----------------------------------------------------------------------- | |
844 | */ | |
845 | void | |
846 | Arch_FindLib (gn, path) | |
847 | GNode *gn; /* Node of library to find */ | |
848 | Lst path; /* Search path */ | |
849 | { | |
850 | char *libName; /* file name for archive */ | |
851 | ||
852 | libName = (char *)emalloc (strlen (gn->name) + 6 - 2); | |
853 | sprintf(libName, "lib%s.a", &gn->name[2]); | |
854 | ||
855 | gn->path = Dir_FindFile (libName, path); | |
856 | ||
857 | free (libName); | |
858 | ||
859 | #ifdef LIBRARIES | |
860 | Var_Set (TARGET, gn->name, gn); | |
861 | #else | |
862 | Var_Set (TARGET, gn->path == (char *) NULL ? gn->name : gn->path, gn); | |
863 | #endif LIBRARIES | |
864 | } | |
865 | ||
866 | /*- | |
867 | *----------------------------------------------------------------------- | |
868 | * Arch_LibOODate -- | |
869 | * Decide if a node with the OP_LIB attribute is out-of-date. Called | |
870 | * from Make_OODate to make its life easier. | |
871 | * | |
872 | * There are several ways for a library to be out-of-date that are | |
873 | * not available to ordinary files. In addition, there are ways | |
874 | * that are open to regular files that are not available to | |
875 | * libraries. A library that is only used as a source is never | |
876 | * considered out-of-date by itself. This does not preclude the | |
877 | * library's modification time from making its parent be out-of-date. | |
878 | * A library will be considered out-of-date for any of these reasons, | |
879 | * given that it is a target on a dependency line somewhere: | |
880 | * Its modification time is less than that of one of its | |
881 | * sources (gn->mtime < gn->cmtime). | |
882 | * Its modification time is greater than the time at which the | |
883 | * make began (i.e. it's been modified in the course | |
884 | * of the make, probably by archiving). | |
885 | * Its modification time doesn't agree with the modification | |
886 | * time of its RANLIBMAG member (i.e. its table of contents | |
887 | * is out-of-date). | |
888 | * | |
889 | * | |
890 | * Results: | |
891 | * TRUE if the library is out-of-date. FALSE otherwise. | |
892 | * | |
893 | * Side Effects: | |
894 | * The library will be hashed if it hasn't been already. | |
895 | * | |
896 | *----------------------------------------------------------------------- | |
897 | */ | |
898 | Boolean | |
899 | Arch_LibOODate (gn) | |
900 | GNode *gn; /* The library's graph node */ | |
901 | { | |
902 | Boolean oodate; | |
903 | ||
904 | if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { | |
905 | oodate = FALSE; | |
906 | } else if ((gn->mtime > now) || (gn->mtime < gn->cmtime)) { | |
907 | oodate = TRUE; | |
908 | } else { | |
909 | struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ | |
910 | int modTimeTOC; /* The table-of-contents's mod time */ | |
911 | ||
912 | arhPtr = ArchStatMember (gn->path, RANLIBMAG, FALSE); | |
913 | ||
914 | if (arhPtr != (struct ar_hdr *)NULL) { | |
915 | (void)sscanf (arhPtr->ar_date, "%12d", &modTimeTOC); | |
916 | ||
917 | if (DEBUG(ARCH) || DEBUG(MAKE)) { | |
918 | printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); | |
919 | } | |
920 | oodate = (gn->mtime > modTimeTOC); | |
921 | } else { | |
922 | /* | |
923 | * A library w/o a table of contents is out-of-date | |
924 | */ | |
925 | if (DEBUG(ARCH) || DEBUG(MAKE)) { | |
926 | printf("No t.o.c...."); | |
927 | } | |
928 | oodate = TRUE; | |
929 | } | |
930 | } | |
931 | return (oodate); | |
932 | } | |
933 | ||
934 | /*- | |
935 | *----------------------------------------------------------------------- | |
936 | * Arch_Init -- | |
937 | * Initialize things for this module. | |
938 | * | |
939 | * Results: | |
940 | * None. | |
941 | * | |
942 | * Side Effects: | |
943 | * The 'archives' list is initialized. | |
944 | * | |
945 | *----------------------------------------------------------------------- | |
946 | */ | |
947 | void | |
948 | Arch_Init () | |
949 | { | |
950 | archives = Lst_Init (FALSE); | |
951 | } |