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