diff options
Diffstat (limited to 'dmake/unix/arlib.c')
-rw-r--r-- | dmake/unix/arlib.c | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/dmake/unix/arlib.c b/dmake/unix/arlib.c new file mode 100644 index 000000000000..3a667f08188f --- /dev/null +++ b/dmake/unix/arlib.c @@ -0,0 +1,610 @@ +/* $RCSfile: arlib.c,v $ +-- $Revision: 1.5 $ +-- last change: $Author: hr $ $Date: 2006-04-20 12:18:37 $ +-- +-- SYNOPSIS +-- Unix archive manipulation code. +-- +-- DESCRIPTION +-- Originally this code was provided by Eric Gisin of MKS. I took +-- his code and completely rewrote it adding cacheing of lib members +-- and other various optimizations. I kept the overal functional +-- idea of the library routines as they are similar to those in GNU +-- make and felt it advantageous to maintain a similar interface. +-- +-- AUTHOR +-- Dennis Vadura, dvadura@dmake.wticorp.com +-- +-- WWW +-- http://dmake.wticorp.com/ +-- +-- COPYRIGHT +-- Copyright (c) 1996,1997 by WTI Corp. All rights reserved. +-- +-- This program is NOT free software; you can redistribute it and/or +-- modify it under the terms of the Software License Agreement Provided +-- in the file <distribution-root>/readme/license.txt. +-- +-- LOG +-- Use cvs log to obtain detailed change logs. +*/ + +/* Sun unix on 386i's has a broken ar.h that does not assume PORTAR format + * by default, so we fix it here. */ +#if defined(i386) || defined(__DGUX__) +#define PORTAR 1 +#endif + +#if !defined (COHERENT) && !defined(__COHERENT__) +#include <ar.h> +#else +#include <arcoff.h> +#endif /* COHERENT, __COHERENT__ */ +#include "extern.h" +#include "sysintf.h" + +/* By defining the defines below it is possible to configure the library + * code for library cacheing/non-cacheing, ASCII archive headers, and a full + * decode of the ar_hdr fields in the scan_ar function. */ + +#ifndef ASCARCH +#define ASCARCH 1 /* ASCII time stored in archive */ +#endif + +#ifndef LC +#define LC 1 /* Turn on library cacheing */ +#endif + +#ifndef CHECKELF +#define CHECKELF 1 /* Enable Elf long member names */ +#endif + +#ifndef DECODE_ALL_AR_FIELDS +#define DECODE_ALL_AR_FIELDS 0 /* decode only fields make needs*/ +#endif + +#ifndef AR_TRUNCATE_MEMBER_NAMES +#define AR_TRUNCATE_MEMBER_NAMES 0 /* truncate member names for */ +#endif /* comparison. */ + +#if LC +# define FOUND_MEMBER FALSE +#else +# define FOUND_MEMBER TRUE +# define _cache_member(a, b, c) +# define _check_cache(a, b, c, d) FALSE +#endif + +#define MAXFNAME 255 /* Max length of member name */ +#define MAXMNAME 8 /* Max module name < MAXFNAME */ + + +/* This struct is used to pass the library and member inrmation about the + * routines that perform the library seeking/cacheing */ +struct ar_args { + char *lib; + char *member; + time_t time; +}; + + +typedef struct AR { + char ar_name[MAXFNAME+1]; /* File name */ + long ar_size; /* Size in bytes */ + time_t ar_time; /* Modification time */ + +#ifdef DOS + char ar_modname[MAXMNAME+1]; /* DOS module name */ +#endif + +#if DECODE_ALL_AR_FIELDS + uint16 ar_mode; /* File mode */ + uint16 ar_uid; /* File owner */ + uint16 ar_gid; /* File group owner */ +#endif +} AR, *ARPTR; + + +static int ar_scan ANSI((FILE *, + int (*) ANSI((FILE *, struct AR *,struct ar_args *)), + struct ar_args *)); +static int ar_touch ANSI(( FILE *, time_t )); +static int time_function ANSI(( FILE *, struct AR *, struct ar_args * )); +static int touch_function ANSI(( FILE *, struct AR *, struct ar_args * )); +static int ar_name_equal ANSI((char *, char *)); + +#if LC +static int _cache_member ANSI((char *, char *, time_t)); +static int _check_cache ANSI((char *, char *, time_t *, int)); +#endif + +/* decoded archive header */ +static AR _ar; +static off_t arhdroffset; /* member seek offset */ + + +PUBLIC time_t +seek_arch(name, lib)/* +====================== + Look for module 'name' inside 'lib'. If compiled with cacheing then first + check to see if the specified lib is cached. If so then return that time + stamp instead of looking into the library. */ +char *name; +char *lib; +{ + FILE *f; + int rv; + time_t mtime; + struct ar_args args; + + /* Check the cache first (if there is a cache) */ + if( _check_cache(name, lib, &mtime, FALSE) ) return( mtime ); + + /* Open the lib file and perform the scan of the members, looking + * for our particular member. If cacheing is enabled it will be + * taken care of automatically during the scan. */ + + args.lib = lib; + args.member = name; + args.time = (time_t)0L; + + if( (f = fopen(lib, "r")) == NIL(FILE) ) return( (time_t)0L ); + rv = ar_scan(f, time_function, &args ); + fclose( f ); + + if( rv < 0 ) Fatal("(%s): Invalid library format", lib); + + return( args.time ); +} + + +PUBLIC int +touch_arch(name, lib)/* +======================= + Look for module 'name' inside 'lib'. If compiled with cacheing then first + check to see if the specified lib is cached. If so then set that time + stamp and write it into the library. Returns 0 on success, non-zero + on failure. */ +char *name; +char *lib; +{ + FILE *f; + int rv; + struct ar_args args; + + /* Open the lib file and perform the scan of the members, looking + * for our particular member. If cacheing is enabled it will be + * taken care of automatically during the scan. */ + + args.lib = lib; + args.member = name; + args.time = (time_t)0L; + + if( (f = fopen(lib, "r+")) == NIL(FILE) ) return( (time_t)1L ); + rv = ar_scan(f, touch_function, &args ); + fclose( f ); + + if( rv < 0 ) Fatal("(%s): Invalid library format", lib); + + return( 0 ); +} + + + +static int +time_function(f, arp, argp)/* +============================= + get library member's time, if it matches than return it in argp, if + cacheing is enabled than cache the library members also. */ +FILE *f; /* library file */ +struct AR *arp; /* library member header */ +struct ar_args *argp; +{ + int rv = _cache_member( arp->ar_name, argp->lib, arp->ar_time ); + + if( ar_name_equal (argp->member, arp->ar_name)) { + argp->time = arp->ar_time; + + if( arp->ar_time == 0 && !(Glob_attr & A_SILENT) ) + Warning( "(%s): Can't extract library member timestamp; using EPOCH", + argp->member); + + return( rv ); /* 1 => no cacheing, 0 => cacheing */ + } + + return( FALSE ); /* continue scan */ +} + + + +static int +touch_function(f, arp, argp)/* +============================== + Update library member's time stamp, and write new time value into cache + if required. */ +FILE *f; /* library file */ +struct AR *arp; /* library member header */ +struct ar_args *argp; +{ + extern time_t time ANSI(( time_t * )); + time_t now = time((time_t*) NULL); /* Current time. */ + + if( ar_name_equal(argp->member, arp->ar_name) ) { + _check_cache( argp->member, argp->lib, &now, TRUE ); + ar_touch(f, now ); + + return( TRUE ); + } + + return( FALSE ); /* continue scan */ +} + + +static int +ar_name_equal (char * name1, char * name2) +{ + int equal; + +#if AR_TRUNCATE_MEMBER_NAMES + struct ar_hdr hdr; + + equal = !strncmp (name1, name2, sizeof (hdr.ar_name)-1); +#else + equal = !strcmp (name1, name2); +#endif + + return equal; +} + + +static int +ar_scan(f, function, arg)/* +=========================== + Scan the opened archive, and call the given function for each member found. + The function will be called with the file positioned at the beginning of + the member and it can read up to arp->ar_size bytes of the archive member. + If the function returns 1, we stop and return 1. We return 0 at the end + of the archive, or -1 if the archive has invalid format. This interface + is more general than required by "make", but it can be used by other + utilities. */ +register FILE *f; +int (*function) ANSI((FILE *, struct AR *, struct ar_args *)); +struct ar_args *arg; +{ + extern long atol (); + register char *p; + struct ar_hdr arhdr; /* archive member header */ + long nsize; /* size of member name */ + long arind=0; /* archive index offset */ + int process; +#if defined(_AIX) + struct fl_hdr flhdr; /* archive file header */ + char magic[SAIAMAG]; /* size of magic string */ +#else +#if ASCARCH + char magic[SARMAG]; +#else + unsigned short word; +#endif +#endif + + fseek( f, 0L, 0 ); /* Start at the beginning of the archive file */ + +#if ASCARCH +#if defined(_AIX) + fread( (char *)&flhdr, sizeof(flhdr), 1, f ); + if( strncmp(flhdr.fl_magic,AIAMAG, SAIAMAG) != 0 ) return(-1); + fseek(f, atol(flhdr.fl_fstmoff), 0 ); /* postition to first member */ +#else + fread( magic, sizeof(magic), 1, f ); + if( strncmp(magic, ARMAG, SARMAG) != 0 ) return( -1 ); +#endif +#else + fread( (char*)&word, sizeof(word), 1, f ); + if( word != ARMAG ) return( -1 ); +#endif + + /* scan the library, calling `function' for each member + */ + while( 1 ) { + arhdroffset = ftell(f); +#if defined(_AIX) + if( fread((char*)&arhdr,sizeof(arhdr)-sizeof(arhdr._ar_name),1,f)!=1) + break; + nsize = atoi(arhdr.ar_namlen); + fseek(f, arhdroffset+(unsigned long)(((struct ar_hdr *)0)->_ar_name.ar_name), 0); + if( fread((char*)_ar.ar_name,nsize,1,f)!=1) + break; + _ar.ar_name[nsize]='\0'; +#else + if( fread((char*) &arhdr, sizeof(arhdr), 1, f) != 1 ) break; + strncpy(_ar.ar_name, arhdr.ar_name, nsize = sizeof(arhdr.ar_name)); +#endif + + for( p = &_ar.ar_name[nsize]; + --p >= _ar.ar_name && *p == ' ';); + + p[1] = '\0'; + if( *p == '/' ) *p = 0; /* SysV has trailing '/' */ + + /* check to see if this is an archive index using SsysV Index scheme. + * see ar(4) man page for more info */ +#if CHECKELF + if( _ar.ar_name[0] == '/' && _ar.ar_name[1] == '\0' ) { + arind = arhdroffset+sizeof(arhdr); + process = 0; + } + else +#endif + process = 1; + +#if !defined(_AIX) +#if ASCARCH + if( strncmp(arhdr.ar_fmag, ARFMAG, sizeof(arhdr.ar_fmag)) != 0 ) + return( -1 ); + _ar.ar_time = atol(arhdr.ar_date); + _ar.ar_size = atol(arhdr.ar_size); +#else + _ar.ar_time = arhdr.ar_date; + _ar.ar_size = arhdr.ar_size; +#endif +#if CHECKELF + /* check for names of the form /xxxx where xxxx is an offset into the + * name table pointed at by arind. */ + if(arind && _ar.ar_name[0] == '/') { + long offset = atol(_ar.ar_name+1); + long here = ftell(f); + int c; + + fseek(f, arind+offset, 0); + p = _ar.ar_name; + while((c=fgetc(f)) != EOF) { + *p++ = c; + if(c == '/') { + p[-1] = '\0'; + break; + } + } + + if (c==EOF) return(-1); /* 'c' should never be EOF */ + fseek(f, here, 0); + } +#endif +#else +#if ASCARCH + _ar.ar_time = atol(arhdr.ar_date); + _ar.ar_size = atol(arhdr.ar_nxtmem); +#else + _ar.ar_time = arhdr.ar_date; + _ar.ar_size = arhdr.ar_nxtmem; +#endif +#endif + + +#if DECODE_ALL_AR_FIELDS +#if ASCARCH + _ar.ar_mode = atoi(arhdr.ar_mode); + _ar.ar_uid = atoi(arhdr.ar_uid); + _ar.ar_gid = atoi(arhdr.ar_gid); +#else + _ar.ar_mode = arhdr.ar_mode; + _ar.ar_uid = arhdr.ar_uid; + _ar.ar_gid = arhdr.ar_gid; +#endif +#endif + if( process && (*function)(f, &_ar, arg) ) return( 1 ); + +#if defined(_AIX) + if( _ar.ar_size == 0L ) break; + fseek( f, (long) _ar.ar_size, 0 ); +#else + fseek( f, arhdroffset + sizeof(arhdr) + ((_ar.ar_size+1) & ~1L), 0 ); +#endif + } + +#if !defined(_AIX) + if( !feof(f) ) return( -1 ); +#endif + return 0; +} + + + +static int +ar_touch( f, now )/* +==================== + touch module header timestamp. */ +FILE *f; +time_t now; +{ + + fseek(f, arhdroffset + (unsigned long)(((struct ar_hdr *)0)->ar_date), 0); + +#if ASCARCH + fprintf(f, "%lu", now); +#else + fwrite((char *)now, sizeof(now), 1, f); +#endif + + return( ferror(f) ? 0 : 1 ); +} + + +#if LC +typedef struct mem { + time_t m_time; /* modify time of member*/ + struct mem *m_next; /* next member in lib */ + char m_valid; /* valid cache entry */ + char m_name[1]; /* lib member name */ +} MEM, *MEMPTR; + +typedef struct lib { + struct lib *lb_next; /* next library in list */ + struct mem *lb_members; /* list of lib members */ + char lb_valid; /* valid cache entry */ + char *lb_name; /* library name */ +} LIB, *LIBPTR; + +static LIBPTR _cache = NIL(LIB); +static MEMPTR _find_member ANSI(( LIBPTR, char * )); + +static int +_check_cache( name, lib, pmtime, touch )/* +========================================== + Check to see if we have cached member in lib, if so return time in pmtime + and return TRUE, otherwise return FALSE, if touch is TRUE then touch + the archive member instead. */ +char *name; +char *lib; +time_t *pmtime; +int touch; +{ + register MEMPTR mp; + register LIBPTR lp; + + for( lp=_cache; lp != NIL(LIB) && lp->lb_name != lib; lp=lp->lb_next ); + if( lp == NIL(LIB) ) return( FALSE ); + + mp = _find_member( lp, name ); + if( mp == NIL(MEM) || !mp->m_valid ) return( FALSE ); + + if( touch == TRUE ) + { + mp->m_time = *pmtime; + mp->m_valid = 1; + } + else + *pmtime = mp->m_time; + + lp->lb_valid = 1; + lp->lb_members = mp; + + return( TRUE ); +} + + + +static int +_cache_member( name, lib, mtime )/* +=================================== + Cache name in lib along with it's time */ +char *name; +char *lib; +time_t mtime; +{ + register MEMPTR mp; + register LIBPTR lp; + + for( lp=_cache; + lp != NIL(LIB) && lp->lb_name != NIL(char) && lp->lb_name != lib; + lp=lp->lb_next); + + if( lp == NIL(LIB) ) + { + lp = (LIBPTR) malloc(sizeof(LIB)); + if( lp == NIL(LIB) ) No_ram(); + + lp->lb_name = lib; + lp->lb_members = NIL(MEM); + lp->lb_next = _cache; + lp->lb_valid = 0; + _cache = lp; + } + + /* On UNIX ar does not allow multiple copies of the same .o file to live + * in the same AR file. If this is not TRUE then use the commented out + * version to set the value of mp. */ + + /*mp = _find_member(lp, name);*/ + mp = NIL(MEM); + + if( mp == NIL(MEM) ) + { + mp = (MEMPTR) malloc(sizeof(char)*offsetof(MEM,m_name[strlen(name)+1])); + if( mp == NIL(MEM) ) No_ram(); + + strcpy( mp->m_name, name ); + mp->m_time = mtime; + + if( lp->lb_members == NIL(MEM) ) { + mp->m_next = mp; + lp->lb_members = mp; + } + else { + mp->m_next = lp->lb_members->m_next; + lp->lb_members->m_next = mp; + lp->lb_members = mp; + } + } + else + mp->m_time = mtime; + + mp->m_valid = 1; + + return( lp->lb_valid ); +} + + +static MEMPTR +_find_member( lp, name ) +LIBPTR lp; +char *name; +{ + register MEMPTR mp = lp->lb_members; + + if( mp == NIL(MEM) ) return(mp); + + do { + if( !strcmp(mp->m_name, name ) ) return( mp ); + mp = mp->m_next; + } + while( mp != lp->lb_members ); + + return( NIL(MEM) ); +} +#endif + + + +PUBLIC void +void_lcache( lib, member )/* +============================ + Void the library cache for lib. If member is NIL(char) then nuke all + of the members, if member is NOT NIL(char) then invalidate only that + member. */ +char *lib; +char *member; +{ +#if LC + register LIBPTR lp; + register MEMPTR mp; + register MEMPTR tmp; + + for( lp=_cache; lp != NIL(LIB) && lp->lb_name != lib; lp=lp->lb_next ); + if( lp == NIL(LIB) ) return; + + if( member == NIL(char) ) { + mp = lp->lb_members; + do { + tmp = mp->m_next; + (void) free( mp ); + mp = tmp; + } while( mp != lp->lb_members ); + + lp->lb_valid = 0; + lp->lb_members = NIL(MEM); + lp->lb_name = NIL(char); + } + else { + mp=lp->lb_members; + do { + if( strcmp( member, mp->m_name) == 0 ) { + lp->lb_members = mp->m_next; + mp->m_valid = 0; + } + + mp=mp->m_next; + } while( mp != lp->lb_members ); + } +#endif +} |