Line data Source code
1 : /*
2 : --
3 : -- SYNOPSIS
4 : -- Unix archive manipulation code.
5 : --
6 : -- DESCRIPTION
7 : -- Originally this code was provided by Eric Gisin of MKS. I took
8 : -- his code and completely rewrote it adding cacheing of lib members
9 : -- and other various optimizations. I kept the overal functional
10 : -- idea of the library routines as they are similar to those in GNU
11 : -- make and felt it advantageous to maintain a similar interface.
12 : --
13 : -- AUTHOR
14 : -- Dennis Vadura, dvadura@dmake.wticorp.com
15 : --
16 : -- WWW
17 : -- http://dmake.wticorp.com/
18 : --
19 : -- COPYRIGHT
20 : -- Copyright (c) 1996,1997 by WTI Corp. All rights reserved.
21 : --
22 : -- This program is NOT free software; you can redistribute it and/or
23 : -- modify it under the terms of the Software License Agreement Provided
24 : -- in the file <distribution-root>/readme/license.txt.
25 : --
26 : -- LOG
27 : -- Use cvs log to obtain detailed change logs.
28 : */
29 :
30 : /* Sun unix on 386i's has a broken ar.h that does not assume PORTAR format
31 : * by default, so we fix it here. */
32 : #if defined(i386) || defined(__DGUX__)
33 : #define PORTAR 1
34 : #endif
35 :
36 : #if !defined (COHERENT) && !defined(__COHERENT__)
37 : #include <ar.h>
38 : #else
39 : #include <arcoff.h>
40 : #endif /* COHERENT, __COHERENT__ */
41 : #include "extern.h"
42 : #include "sysintf.h"
43 :
44 : /* By defining the defines below it is possible to configure the library
45 : * code for library cacheing/non-cacheing, ASCII archive headers, and a full
46 : * decode of the ar_hdr fields in the scan_ar function. */
47 :
48 : #ifndef ASCARCH
49 : #define ASCARCH 1 /* ASCII time stored in archive */
50 : #endif
51 :
52 : #ifndef LC
53 : #define LC 1 /* Turn on library cacheing */
54 : #endif
55 :
56 : #ifndef CHECKELF
57 : #define CHECKELF 1 /* Enable Elf long member names */
58 : #endif
59 :
60 : #ifndef DECODE_ALL_AR_FIELDS
61 : #define DECODE_ALL_AR_FIELDS 0 /* decode only fields make needs*/
62 : #endif
63 :
64 : #ifndef AR_TRUNCATE_MEMBER_NAMES
65 : #define AR_TRUNCATE_MEMBER_NAMES 0 /* truncate member names for */
66 : #endif /* comparison. */
67 :
68 : #if LC
69 : # define FOUND_MEMBER FALSE
70 : #else
71 : # define FOUND_MEMBER TRUE
72 : # define _cache_member(a, b, c)
73 : # define _check_cache(a, b, c, d) FALSE
74 : #endif
75 :
76 : #define MAXFNAME 255 /* Max length of member name */
77 : #define MAXMNAME 8 /* Max module name < MAXFNAME */
78 :
79 :
80 : /* This struct is used to pass the library and member inrmation about the
81 : * routines that perform the library seeking/cacheing */
82 : struct ar_args {
83 : char *lib;
84 : char *member;
85 : time_t time;
86 : };
87 :
88 :
89 : typedef struct AR {
90 : char ar_name[MAXFNAME+1]; /* File name */
91 : long ar_size; /* Size in bytes */
92 : time_t ar_time; /* Modification time */
93 :
94 : #ifdef DOS
95 : char ar_modname[MAXMNAME+1]; /* DOS module name */
96 : #endif
97 :
98 : #if DECODE_ALL_AR_FIELDS
99 : uint16 ar_mode; /* File mode */
100 : uint16 ar_uid; /* File owner */
101 : uint16 ar_gid; /* File group owner */
102 : #endif
103 : } AR, *ARPTR;
104 :
105 :
106 : static int ar_scan ANSI((FILE *,
107 : int (*) ANSI((FILE *, struct AR *,struct ar_args *)),
108 : struct ar_args *));
109 : static int ar_touch ANSI(( FILE *, time_t ));
110 : static int time_function ANSI(( FILE *, struct AR *, struct ar_args * ));
111 : static int touch_function ANSI(( FILE *, struct AR *, struct ar_args * ));
112 : static int ar_name_equal ANSI((char *, char *));
113 :
114 : #if LC
115 : static int _cache_member ANSI((char *, char *, time_t));
116 : static int _check_cache ANSI((char *, char *, time_t *, int));
117 : #endif
118 :
119 : /* decoded archive header */
120 : static AR _ar;
121 : static off_t arhdroffset; /* member seek offset */
122 :
123 :
124 : PUBLIC time_t
125 0 : seek_arch(name, lib)/*
126 : ======================
127 : Look for module 'name' inside 'lib'. If compiled with cacheing then first
128 : check to see if the specified lib is cached. If so then return that time
129 : stamp instead of looking into the library. */
130 : char *name;
131 : char *lib;
132 : {
133 : FILE *f;
134 : int rv;
135 : time_t mtime;
136 : struct ar_args args;
137 :
138 : /* Check the cache first (if there is a cache) */
139 0 : if( _check_cache(name, lib, &mtime, FALSE) ) return( mtime );
140 :
141 : /* Open the lib file and perform the scan of the members, looking
142 : * for our particular member. If cacheing is enabled it will be
143 : * taken care of automatically during the scan. */
144 :
145 0 : args.lib = lib;
146 0 : args.member = name;
147 0 : args.time = (time_t)0L;
148 :
149 0 : if( (f = fopen(lib, "r")) == NIL(FILE) ) return( (time_t)0L );
150 0 : rv = ar_scan(f, time_function, &args );
151 0 : fclose( f );
152 :
153 0 : if( rv < 0 ) Fatal("(%s): Invalid library format", lib);
154 :
155 0 : return( args.time );
156 : }
157 :
158 :
159 : PUBLIC int
160 0 : touch_arch(name, lib)/*
161 : =======================
162 : Look for module 'name' inside 'lib'. If compiled with cacheing then first
163 : check to see if the specified lib is cached. If so then set that time
164 : stamp and write it into the library. Returns 0 on success, non-zero
165 : on failure. */
166 : char *name;
167 : char *lib;
168 : {
169 : FILE *f;
170 : int rv;
171 : struct ar_args args;
172 :
173 : /* Open the lib file and perform the scan of the members, looking
174 : * for our particular member. If cacheing is enabled it will be
175 : * taken care of automatically during the scan. */
176 :
177 0 : args.lib = lib;
178 0 : args.member = name;
179 0 : args.time = (time_t)0L;
180 :
181 0 : if( (f = fopen(lib, "r+")) == NIL(FILE) ) return( (time_t)1L );
182 0 : rv = ar_scan(f, touch_function, &args );
183 0 : fclose( f );
184 :
185 0 : if( rv < 0 ) Fatal("(%s): Invalid library format", lib);
186 :
187 0 : return( 0 );
188 : }
189 :
190 :
191 :
192 : static int
193 0 : time_function(f, arp, argp)/*
194 : =============================
195 : get library member's time, if it matches than return it in argp, if
196 : cacheing is enabled than cache the library members also. */
197 : FILE *f; /* library file */
198 : struct AR *arp; /* library member header */
199 : struct ar_args *argp;
200 : {
201 0 : int rv = _cache_member( arp->ar_name, argp->lib, arp->ar_time );
202 :
203 0 : if( ar_name_equal (argp->member, arp->ar_name)) {
204 0 : argp->time = arp->ar_time;
205 :
206 0 : if( arp->ar_time == 0 && !(Glob_attr & A_SILENT) )
207 0 : Warning( "(%s): Can't extract library member timestamp; using EPOCH",
208 : argp->member);
209 :
210 0 : return( rv ); /* 1 => no cacheing, 0 => cacheing */
211 : }
212 :
213 0 : return( FALSE ); /* continue scan */
214 : }
215 :
216 :
217 :
218 : static int
219 0 : touch_function(f, arp, argp)/*
220 : ==============================
221 : Update library member's time stamp, and write new time value into cache
222 : if required. */
223 : FILE *f; /* library file */
224 : struct AR *arp; /* library member header */
225 : struct ar_args *argp;
226 : {
227 : extern time_t time ANSI(( time_t * ));
228 0 : time_t now = time((time_t*) NULL); /* Current time. */
229 :
230 0 : if( ar_name_equal(argp->member, arp->ar_name) ) {
231 0 : _check_cache( argp->member, argp->lib, &now, TRUE );
232 0 : ar_touch(f, now );
233 :
234 0 : return( TRUE );
235 : }
236 :
237 0 : return( FALSE ); /* continue scan */
238 : }
239 :
240 :
241 : static int
242 0 : ar_name_equal (char * name1, char * name2)
243 : {
244 : int equal;
245 :
246 : #if AR_TRUNCATE_MEMBER_NAMES
247 : struct ar_hdr hdr;
248 :
249 : equal = !strncmp (name1, name2, sizeof (hdr.ar_name)-1);
250 : #else
251 0 : equal = !strcmp (name1, name2);
252 : #endif
253 :
254 0 : return equal;
255 : }
256 :
257 :
258 : static int
259 0 : ar_scan(f, function, arg)/*
260 : ===========================
261 : Scan the opened archive, and call the given function for each member found.
262 : The function will be called with the file positioned at the beginning of
263 : the member and it can read up to arp->ar_size bytes of the archive member.
264 : If the function returns 1, we stop and return 1. We return 0 at the end
265 : of the archive, or -1 if the archive has invalid format. This interface
266 : is more general than required by "make", but it can be used by other
267 : utilities. */
268 : register FILE *f;
269 : int (*function) ANSI((FILE *, struct AR *, struct ar_args *));
270 : struct ar_args *arg;
271 : {
272 : extern long atol ();
273 : register char *p;
274 : struct ar_hdr arhdr; /* archive member header */
275 : long nsize; /* size of member name */
276 0 : long arind=0; /* archive index offset */
277 : int process;
278 : #if defined(_AIX)
279 : struct fl_hdr flhdr; /* archive file header */
280 : char magic[SAIAMAG]; /* size of magic string */
281 : #else
282 : #if ASCARCH
283 : char magic[SARMAG];
284 : #else
285 : unsigned short word;
286 : #endif
287 : #endif
288 :
289 0 : fseek( f, 0L, 0 ); /* Start at the beginning of the archive file */
290 :
291 : #if ASCARCH
292 : #if defined(_AIX)
293 : if( fread( (char *)&flhdr, sizeof(flhdr), 1, f ) != 1 ) return (-1);
294 : if( strncmp(flhdr.fl_magic,AIAMAG, SAIAMAG) != 0 ) return(-1);
295 : fseek(f, atol(flhdr.fl_fstmoff), 0 ); /* postition to first member */
296 : #else
297 0 : if( fread( magic, sizeof(magic), 1, f ) != 1 ) return( -1 );
298 0 : if( strncmp(magic, ARMAG, SARMAG) != 0 ) return( -1 );
299 : #endif
300 : #else
301 : if( fread( (char*)&word, sizeof(word), 1, f ) != 1 ) return( -1 );
302 : if( word != ARMAG ) return( -1 );
303 : #endif
304 :
305 : /* scan the library, calling `function' for each member
306 : */
307 : while( 1 ) {
308 0 : arhdroffset = ftell(f);
309 : #if defined(_AIX)
310 : if( fread((char*)&arhdr,sizeof(arhdr)-sizeof(arhdr._ar_name),1,f)!=1)
311 : break;
312 : nsize = atoi(arhdr.ar_namlen);
313 : fseek(f, arhdroffset+(unsigned long)(((struct ar_hdr *)0)->_ar_name.ar_name), 0);
314 : if( fread((char*)_ar.ar_name,nsize,1,f)!=1)
315 : break;
316 : _ar.ar_name[nsize]='\0';
317 : #else
318 0 : if( fread((char*) &arhdr, sizeof(arhdr), 1, f) != 1 ) break;
319 0 : strncpy(_ar.ar_name, arhdr.ar_name, nsize = sizeof(arhdr.ar_name));
320 : #endif
321 :
322 0 : for( p = &_ar.ar_name[nsize];
323 0 : --p >= _ar.ar_name && *p == ' ';);
324 :
325 0 : p[1] = '\0';
326 0 : if( *p == '/' ) *p = 0; /* SysV has trailing '/' */
327 :
328 : /* check to see if this is an archive index using SsysV Index scheme.
329 : * see ar(4) man page for more info */
330 : #if CHECKELF
331 0 : if( _ar.ar_name[0] == '/' && _ar.ar_name[1] == '\0' ) {
332 0 : arind = arhdroffset+sizeof(arhdr);
333 0 : process = 0;
334 : }
335 : else
336 : #endif
337 0 : process = 1;
338 :
339 : #if !defined(_AIX)
340 : #if ASCARCH
341 0 : if( strncmp(arhdr.ar_fmag, ARFMAG, sizeof(arhdr.ar_fmag)) != 0 )
342 0 : return( -1 );
343 0 : _ar.ar_time = atol(arhdr.ar_date);
344 0 : _ar.ar_size = atol(arhdr.ar_size);
345 : #else
346 : _ar.ar_time = arhdr.ar_date;
347 : _ar.ar_size = arhdr.ar_size;
348 : #endif
349 : #if CHECKELF
350 : /* check for names of the form /xxxx where xxxx is an offset into the
351 : * name table pointed at by arind. */
352 0 : if(arind && _ar.ar_name[0] == '/') {
353 0 : long offset = atol(_ar.ar_name+1);
354 0 : long here = ftell(f);
355 : int c;
356 :
357 0 : fseek(f, arind+offset, 0);
358 0 : p = _ar.ar_name;
359 0 : while((c=fgetc(f)) != EOF) {
360 0 : *p++ = c;
361 0 : if(c == '/') {
362 0 : p[-1] = '\0';
363 0 : break;
364 : }
365 : }
366 :
367 0 : if (c==EOF) return(-1); /* 'c' should never be EOF */
368 0 : fseek(f, here, 0);
369 : }
370 : #endif
371 : #else
372 : #if ASCARCH
373 : _ar.ar_time = atol(arhdr.ar_date);
374 : _ar.ar_size = atol(arhdr.ar_nxtmem);
375 : #else
376 : _ar.ar_time = arhdr.ar_date;
377 : _ar.ar_size = arhdr.ar_nxtmem;
378 : #endif
379 : #endif
380 :
381 :
382 : #if DECODE_ALL_AR_FIELDS
383 : #if ASCARCH
384 : _ar.ar_mode = atoi(arhdr.ar_mode);
385 : _ar.ar_uid = atoi(arhdr.ar_uid);
386 : _ar.ar_gid = atoi(arhdr.ar_gid);
387 : #else
388 : _ar.ar_mode = arhdr.ar_mode;
389 : _ar.ar_uid = arhdr.ar_uid;
390 : _ar.ar_gid = arhdr.ar_gid;
391 : #endif
392 : #endif
393 0 : if( process && (*function)(f, &_ar, arg) ) return( 1 );
394 :
395 : #if defined(_AIX)
396 : if( _ar.ar_size == 0L ) break;
397 : fseek( f, (long) _ar.ar_size, 0 );
398 : #else
399 0 : fseek( f, arhdroffset + sizeof(arhdr) + ((_ar.ar_size+1) & ~1L), 0 );
400 : #endif
401 0 : }
402 :
403 : #if !defined(_AIX)
404 0 : if( !feof(f) ) return( -1 );
405 : #endif
406 0 : return 0;
407 : }
408 :
409 :
410 :
411 : static int
412 0 : ar_touch( f, now )/*
413 : ====================
414 : touch module header timestamp. */
415 : FILE *f;
416 : time_t now;
417 : {
418 :
419 0 : fseek(f, arhdroffset + (unsigned long)(((struct ar_hdr *)0)->ar_date), 0);
420 :
421 : #if ASCARCH
422 0 : fprintf(f, "%lu", now);
423 : #else
424 : fwrite((char *)now, sizeof(now), 1, f);
425 : #endif
426 :
427 0 : return( ferror(f) ? 0 : 1 );
428 : }
429 :
430 :
431 : #if LC
432 : typedef struct mem {
433 : time_t m_time; /* modify time of member*/
434 : struct mem *m_next; /* next member in lib */
435 : char m_valid; /* valid cache entry */
436 : char m_name[1]; /* lib member name */
437 : } MEM, *MEMPTR;
438 :
439 : typedef struct lib {
440 : struct lib *lb_next; /* next library in list */
441 : struct mem *lb_members; /* list of lib members */
442 : char lb_valid; /* valid cache entry */
443 : char *lb_name; /* library name */
444 : } LIB, *LIBPTR;
445 :
446 : static LIBPTR _cache = NIL(LIB);
447 : static MEMPTR _find_member ANSI(( LIBPTR, char * ));
448 :
449 : static int
450 0 : _check_cache( name, lib, pmtime, touch )/*
451 : ==========================================
452 : Check to see if we have cached member in lib, if so return time in pmtime
453 : and return TRUE, otherwise return FALSE, if touch is TRUE then touch
454 : the archive member instead. */
455 : char *name;
456 : char *lib;
457 : time_t *pmtime;
458 : int touch;
459 : {
460 : register MEMPTR mp;
461 : register LIBPTR lp;
462 :
463 0 : for( lp=_cache; lp != NIL(LIB) && lp->lb_name != lib; lp=lp->lb_next );
464 0 : if( lp == NIL(LIB) ) return( FALSE );
465 :
466 0 : mp = _find_member( lp, name );
467 0 : if( mp == NIL(MEM) || !mp->m_valid ) return( FALSE );
468 :
469 0 : if( touch == TRUE )
470 : {
471 0 : mp->m_time = *pmtime;
472 0 : mp->m_valid = 1;
473 : }
474 : else
475 0 : *pmtime = mp->m_time;
476 :
477 0 : lp->lb_valid = 1;
478 0 : lp->lb_members = mp;
479 :
480 0 : return( TRUE );
481 : }
482 :
483 :
484 :
485 : static int
486 0 : _cache_member( name, lib, mtime )/*
487 : ===================================
488 : Cache name in lib along with it's time */
489 : char *name;
490 : char *lib;
491 : time_t mtime;
492 : {
493 : register MEMPTR mp;
494 : register LIBPTR lp;
495 :
496 0 : for( lp=_cache;
497 0 : lp != NIL(LIB) && lp->lb_name != NIL(char) && lp->lb_name != lib;
498 0 : lp=lp->lb_next);
499 :
500 0 : if( lp == NIL(LIB) )
501 : {
502 0 : lp = (LIBPTR) malloc(sizeof(LIB));
503 0 : if( lp == NIL(LIB) ) No_ram();
504 :
505 0 : lp->lb_name = lib;
506 0 : lp->lb_members = NIL(MEM);
507 0 : lp->lb_next = _cache;
508 0 : lp->lb_valid = 0;
509 0 : _cache = lp;
510 : }
511 :
512 : /* On UNIX ar does not allow multiple copies of the same .o file to live
513 : * in the same AR file. If this is not TRUE then use the commented out
514 : * version to set the value of mp. */
515 :
516 : /*mp = _find_member(lp, name);*/
517 0 : mp = NIL(MEM);
518 :
519 0 : if( mp == NIL(MEM) )
520 : {
521 0 : mp = (MEMPTR) malloc(sizeof(char)*offsetof(MEM,m_name[strlen(name)+1]));
522 0 : if( mp == NIL(MEM) ) No_ram();
523 :
524 0 : strcpy( mp->m_name, name );
525 0 : mp->m_time = mtime;
526 :
527 0 : if( lp->lb_members == NIL(MEM) ) {
528 0 : mp->m_next = mp;
529 0 : lp->lb_members = mp;
530 : }
531 : else {
532 0 : mp->m_next = lp->lb_members->m_next;
533 0 : lp->lb_members->m_next = mp;
534 0 : lp->lb_members = mp;
535 : }
536 : }
537 : else
538 0 : mp->m_time = mtime;
539 :
540 0 : mp->m_valid = 1;
541 :
542 0 : return( lp->lb_valid );
543 : }
544 :
545 :
546 : static MEMPTR
547 0 : _find_member( lp, name )
548 : LIBPTR lp;
549 : char *name;
550 : {
551 0 : register MEMPTR mp = lp->lb_members;
552 :
553 0 : if( mp == NIL(MEM) ) return(mp);
554 :
555 : do {
556 0 : if( !strcmp(mp->m_name, name ) ) return( mp );
557 0 : mp = mp->m_next;
558 : }
559 0 : while( mp != lp->lb_members );
560 :
561 0 : return( NIL(MEM) );
562 : }
563 : #endif
564 :
565 :
566 :
567 : PUBLIC void
568 0 : void_lcache( lib, member )/*
569 : ============================
570 : Void the library cache for lib. If member is NIL(char) then nuke all
571 : of the members, if member is NOT NIL(char) then invalidate only that
572 : member. */
573 : char *lib;
574 : char *member;
575 : {
576 : #if LC
577 : register LIBPTR lp;
578 : register MEMPTR mp;
579 : register MEMPTR tmp;
580 :
581 0 : for( lp=_cache; lp != NIL(LIB) && lp->lb_name != lib; lp=lp->lb_next );
582 0 : if( lp == NIL(LIB) ) return;
583 :
584 0 : if( member == NIL(char) ) {
585 0 : mp = lp->lb_members;
586 : do {
587 0 : tmp = mp->m_next;
588 0 : (void) free( mp );
589 0 : mp = tmp;
590 0 : } while( mp != lp->lb_members );
591 :
592 0 : lp->lb_valid = 0;
593 0 : lp->lb_members = NIL(MEM);
594 0 : lp->lb_name = NIL(char);
595 : }
596 : else {
597 0 : mp=lp->lb_members;
598 : do {
599 0 : if( strcmp( member, mp->m_name) == 0 ) {
600 0 : lp->lb_members = mp->m_next;
601 0 : mp->m_valid = 0;
602 : }
603 :
604 0 : mp=mp->m_next;
605 0 : } while( mp != lp->lb_members );
606 : }
607 : #endif
608 : }
|