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