#! /bin/sh # This is a shell archive. Type 'sh ' to unpack. echo x - README.ar2 cat >README.ar2 <<'MKSHAR_EOF' README for utility: ar2 Author: Art S. Kagel, 1990 Version: Features version 1.4, Source revision 1.30 File(s): ar2.c, ar2.mk Ar2 was written as a portable ar archiver at a time when GNU ar and GNU tar were not available or not commonly available so that I could transport collections of files from UNIX to DOS and back. Later features were added to allow me to transport between systems with incompatible versions of ar. The main incompatilibilities arrise from the different handling of long filenames in BSD and System V derived versions. BSD systems (notably Ultrix, OSF-1, and AIX) extended the entry header format to be variable lenght to accomodate longer names. System V systems added a long names table at the beginning of the file which contains an offset to the actual file entry which has a zero length name in its actual header. Ar2 can both read and write both BSD (-d) and System V (-g) formats although it cannot append long filename files to an existing System V archive. In addition it supports a third long filename scheme of my own, implemented with the '-i' flag, which is marginally portable to both the other systems. An archive created with '-i' can be extracted successfully with ANY ar utility but the filenames will be truncated (with whatever name clashes that causes) and a single bogus file named '!' will be left around. On reading an archive the header version is automatically detected. Ar2 does not currently support the initial symbol table entry needed to support object libraries (ar's primary use) and is intended for source file transport ONLY. (Although with the appropriate header option the resulting library will not be incompatible with OS utilities used to add the symbol table, ie "ar -s" or ranlib.) Anyone who feels the need to add this capability is welcome to do so and feedback the updates to me. Since it was originally intended for extraction of archives created elsewhere extraction is the default operation so that a line like: ar2 arcfile.ar will extract the contents of the archive named arcfile.ar. My archive naming convention is to use .ar or .ar2 (for -i archives) to distinguish source archives from object libraries (lib*.a). The following is the usage report printed by "ar -?" or just "ar": Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999 Art S. Kagel 222 Dunhams Corner Rd. East Brunswick, N.J. 08816 Written by Art S. Kagel, August 1990. Portable Version 1.4 RCS $Revision: 1.30 $ This program is placed in the public domain for private use only. All commercial rights are retained. Usage: ar2 [-[qmrxtacelni]] arcname [file1 [file 2 [...]]] With no flags ar2 extracts files from a UNIX portable archive. UNIX "ar" compatible flags (keys), as follows: -q Quick append. Add file to end. (This function will include duplicate copies of files.) -m Move file to archive and remove. (This function will include duplicate copies of files.) -r Replace file. Replace file in archive with another version. (Current version replaces all occurances if the file is multiply contained in the archive.) -x Extract files. -t List out contents of archive. Additional (non-compatible) flags available: -a Append file. Synonym for -q. -c Create archive. CREATE a NEW archive or OVERWRITE an existing one! (q, m, a, & r will also CREATE an archive if none exists.) -d Write archive with D.E.C. compatible header. DEC erroneously drops the terminating slash at the end of the file name. While DEC format supports long file names, and ar2 can list and extract such archives, ar2 will truncate long file names it adds to an archive with -d. -e Extract files. Synonym for -x. -g Write archive with Centralized Extended Name table. Permits names longer than 14 characters. (DG/UX ar compatible) Default: Truncate names to 14 chars. Extended names cannot be appended to an existing archive in this format. -l List out files. Synonym for -t. -i Write archive with Distributed Extended Name table. Permits names longer than 14 characters. This format extended name table may be appended to. DEC, DG/UX, and ar2 Extended format archives are recognized transparently for listing and extraction. The -d, -g, and -i options are provided to create and update such archives. Only one of these options may be provided. MKSHAR_EOF echo x - ar2.c cat >ar2.c <<'MKSHAR_EOF' /* ar2_port.c - portable ar archive utility. RCS Header ---------- $Id: ar2_port.c,v 1.30 1999/10/11 15:49:30 kagel Exp $ RCS Log ------- $Log: ar2_port.c,v $ * Revision 1.30 1999/10/11 15:49:30 kagel * Fixed core dump if run without args. * Fixed several warnings, now compiles cleanly. * * Revision 1.29 1993/07/29 19:51:52 kagel * Added RCS headers. * * Revision 1.28 1993/07/28 18:18:38 kagel * Fixed #defines protecting DOS specific code. * * Revision 1.28 1993/07/28 18:18:38 kagel * Fixed #defines protecting DOS specific code. * * Revision 1.27 1993/07/28 18:08:06 kagel * cd test * Bug. * * Revision 1.26 1993/07/28 18:02:00 kagel * Added support to extract files to the BEST filename possible under DOS. * Will tak 1st 8 chars and 3 from any extension. * * Revision 1.25 1993/07/28 16:54:39 kagel * Fixed bug in ar_verify for DG style central string table handling * * Revision 1.24 1993/07/28 14:42:42 kagel * Bug in read_header(). * * Revision 1.23 1993/07/28 14:19:04 kagel * Fixed bug in read_header affecting repl_ar() * * Revision 1.22 1993/07/28 13:01:34 kagel * Extracted common code to read and parse archive headers to a function. * * Revision 1.21 1993/07/22 18:49:16 kagel * Added proper messages for bad filenames and nonexistent files. * * Revision 1.20 1993/07/22 18:36:25 kagel * Made -d -g -i mutually exclusive. Added to usage to better explain these * options. * * Revision 1.19 1993/07/22 18:24:57 kagel * Just fixing the usage strings. * * Revision 1.18 1993/07/12 13:30:56 kagel * Added compatibility with DEC ar format. Implemented -d flag for creation/ * appending/replacing files in archives. Also implemented listing and * extracting of DEC format archives with automatic recognition. When creating * DEC format archives, names are truncated to 14 characters, long names are, * however, handled properly when found on listing or extracting. * * Revision 1.17 1993/07/12 12:18:46 kagel * Modified Log Message - ASK - * Sorry the bug mentioned in Rev 1.14 was mine not theirs. I have fixed this. * Though I'm not certain what was wrong. * * Revision 1.16 1993/07/12 12:02:36 kagel * Fixed bug in -g option which would write out a null string table. This * confused ar2 and DG/UX-ar. * * Revision 1.15 1993/07/12 11:52:20 kagel * *** empty log message *** * * Revision 1.14 1993/07/12 11:46:39 kagel * Added code to support a DG/UX ar bug which fails to end a 14 character name * with the required slash. The DG/UX ar utility expects this lack when reading * archives and truncates terminated names of exactly 14 chars. to zero length. * * Revision 1.13 1993/07/08 20:29:42 kagel * Changed mode string in the string table entries to eliminate spurious error * messages from older ar utilities during extraction. The file named "!" * will now contain the full name of the last extended name file extracted. * All hail compatibility and portability! * * Revision 1.12 1993/07/08 20:20:48 kagel * Modified the header format for extended file name entries to provide * backward compatibility with older ar utilities. An ar2 format archive may * be read and even extracted by standard ar. Listings will contain files * named "!" which represent the string table entries. Extraction will succeed * but, will leave one "!" file around, probably the first. * * Revision 1.11 1993/07/08 19:45:22 kagel * Added list, extract and replace support for DG/UX format extended filename * string tables. This is a central table of long file names. Such a central * table cannot be extended without rewriting the entire archive which is * against the philosophy of this program. Ar2 can, however, create such * archives from scratch as well as list, extract and replace their contents. * * Also added my own version of extended file names. The '-i' flag (for index) * adds a string table like entry before the actual file header for any file * with an oversized (>14 chars) name. This "Distributed" string table can be * easily appended to. This format supports filenames up to 255 characters * and supports listing, extraction, creation, replacement and appending * operations. In the next release I will attempt to make this transparent to * traditional ar utilities. * * Revision 1.10 1993/07/07 14:21:37 kagel * fix * * Revision 1.9 1993/07/07 14:15:27 kagel * *** empty log message *** * * Revision 1.8 1993/07/07 14:13:37 kagel * *** empty log message *** * * Revision 1.7 1993/07/07 14:11:55 kagel * Reformatted code to fit 80 cols and broke long strings using string pasting. * * Revision 1.6 1993/07/07 13:48:30 kagel * Changed to use optarg. * * Revision 1.5 1993/07/07 13:43:37 kagel * *** empty log message *** * * Revision 1.4 1993/07/07 13:38:33 kagel * Formatting. * * Revision 1.3 1993/07/07 12:54:12 kagel * Implemented ability to read and list an archive with an extended file name * string table. Set up to write and update but not fully implemented. * * Revision 1.2 1993/07/06 19:44:45 kagel * Added RCS Header and Log * 03/1993 - ASK Modified to handle D.E.C. Ultrix format headers. These are missing the slash trailing the filename and may contain oversize filenames in place. DEC archives handled with an option. 2/10/1993 - ASK Modified to handle long filenames (mostly under BSD) by truncating to 14 characters with a warning. */ #include #include #include #include #include #include #include #include #include #include #if defined(__TURBOC__) #include #include #else #include #include #endif #ifndef __STDC__ #define void #endif #if !defined( __unix__ ) #define nooptarg #endif /* Defines */ #define ARMAG "!\n" #define SARMAG 8 #define ARFMAG "`\n" #define MYBUFSIZE 32768 #define FALSE (0) #define TRUE (!FALSE) #define OPEN TRUE #define CLOSED FALSE #ifdef __MSDOS__ #define DIRSEP '\\' #else #define DIRSEP '/' #endif #if !defined( SEEK_SET ) #define SEEK_SET 0 #endif #if !defined( SEEK_CUR ) #define SEEK_CUR 1 #endif #if !defined( SEEK_END ) #define SEEK_END 2 #endif /* Process States */ #if defined( noenum ) #define INITIAL 0 #define EXIT 1 #define EXTRACT 2 #define CREATE 4 #define APPEND 8 #define BUILD 16 #define FINISHED 32 #define LISTING 64 #define REPLACE 128 #define StateList int; #define TRUNCATE 0 #define CENTRAL 1 #define DISTRIBUTE 2 #define Naming int #else enum statelist { INITIAL = 0, EXIT = 1, EXTRACT = 2, CREATE = 4, APPEND = 8, BUILD = 16, FINISHED = 32, LISTING = 64, REPLACE = 128 }; typedef enum statelist StateList; enum naming { TRUNCATE = 0, CENTRAL = 1, DISTRIBUTE = 2 }; typedef enum naming Naming; #endif /* Process Control Flags */ #define NORMAL 0 #define UNLINK 1 #define NOFLAGS 2 /* Structure Definitions */ typedef struct AR_HDR { char ar_name[16]; /* file member name - '/' terminated */ char ar_date[12]; /* file member date - decimal */ char ar_uid[6]; /* file member user id - decimal */ char ar_gid[6]; /* file member group id - decimal */ char ar_mode[8]; /* file member mode - octal */ char ar_size[10]; /* file member size - decimal */ char ar_fmag[2]; /* ARFMAG - string to end header */ char nullchar; /* NULL char bucket for fgets to use */ } ar_hdr; /* Forward Reference Declarations */ void usage( char * ); unsigned otoi(); char *ltoa(); int renameit(); int read_ar(); int list_ar(); int repl_ar(); FILE *create_arc(); int write_file(); int ar_verify(); void rewrite_strings( FILE *, int, int, char ** ); int read_header( ar_hdr *, char *, FILE *, long * ); /* Globals - ARGHH (Much as I dislike them!) */ char RCSID[] = "$Id: ar2_port.c,v 1.30 1999/10/11 15:49:30 kagel Exp $", RCSWHAT[] = "$What: <@(#) ar2_port.c,v 1.30> $"; int Flag = NORMAL; extern int errno; int Native = FALSE; int DECFlag = FALSE, TableSize; Naming Extended = TRUNCATE; char *NameTable = (char *)NULL; /* N.B. - Copyright notice in copyright[] MUST be displayed to satisfy */ /* requirements of U.S. Copyright law and the specifics of the */ /* public domain release of this product. In addition the */ /* release requires that users 1) Make no commercial sale of this */ /* program or any program derived from its source. 2) Forward any */ /* modifications, in the form of complete source listings or file */ /* in human or machine readable format to: AR2 Mods c/o Art S. */ /* Kagel at 222 Dunham's Corner Road, E. Brunswick, NJ 08816-3414; */ /* for consideration for future releases. Single user */ /* installations may refrain from displaying the notice at run */ /* time, however it must remain in an array variable included */ /* in the executable program. */ char copyright[] = "\nCopyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999\n" "Art S. Kagel\n222 Dunhams Corner Rd.\n" "East Brunswick, N.J. 08816\n"; char attribution[] ="\n\tWritten by Art S. Kagel, August 1990. " "Portable Version 1.4\n\tRCS $Revision: 1.30 $\n\t" "This program is placed in the public domain for private use only." "\n\tAll commercial rights are retained.\n"; /* Main program function. Parse args and drive functionality. */ int main( argc, argv ) int argc; char **argv; { FILE *out_arc, *in_arc; int i, fdesc, j, ar_arg, FileStatus, k, GotCommand = FALSE; StateList state; char activity[20], temp_name[100], *str_ptr, *str_ptr2; int arg_chr; struct stat fstats; state = INITIAL; FileStatus = CLOSED; if (argc == 2) { Flag = NOFLAGS; state = EXTRACT; } else if (argc < 3) { usage( argv[0] ); state = FINISHED; exit(1); } else { k = 1; #if defined( nooptarg ) while (argv[k][0] == '-' && k < argc) { arg_chr = argv[k][1]; #else while ((arg_chr = getopt( argc, argv, "acdegilmqrtx" )) != EOF) { #endif switch (arg_chr) { case 'a': case 'q': /* Append to existing archive by default */ state = APPEND; strcpy( activity, "a -" ); GotCommand = TRUE; break; case 'c': /* Create new archive */ if (state != INITIAL) goto defcas; state = CREATE; strcpy( activity, "a -" ); GotCommand = TRUE; break; case 'd': /* Digital ULTRIX compatibility flag. ULTRIX ar does not */ /* conform the the ar standard in at least one way: file */ /* names are NOT terminated with a trailing slash! */ /* Currently no-op! */ if (Extended != TRUNCATE) { fprintf( stderr, "Only one of -i -g -d permitted.\n\n" ); usage( argv[0] ); return 22; } DECFlag = TRUE; break; case 'e': case 'x': /* Extract from existing archive */ if (state != INITIAL) goto defcas; state = EXTRACT; strcpy( activity, "x -" ); GotCommand = TRUE; break; case 'i': /* Write extended name archive with distributed string table. This differs from the centralized string table implied by the "-g" option and implemented in DG/UX. This is my own implementation of extended filenames. It places the single entry string table immediately before the related file's header. This has the advantage that the archive can be appended to without rewriting the file and reconstructing the string table for each file added. Reading of archives so extended is automatic. */ if (DECFlag || Extended == CENTRAL) { fprintf( stderr, "Only one of -i -g -d permitted.\n\n" ); usage( argv[0] ); return 22; } Extended = DISTRIBUTE; break; case 'l': case 't': /* List contents of an archive */ if (state != INITIAL) goto defcas; state = LISTING; GotCommand = TRUE; break; case 'r': if (state != INITIAL) goto defcas; state = REPLACE; Flag = REPLACE; strcpy( activity, "r -" ); GotCommand = TRUE; break; case 'm': /* Move file into archive and unlink */ Flag = UNLINK; break; case 'g': /* Write handle archive with extended file name string table */ /* This is used by DG/UX to handle long file names in an ar */ /* archive. Format is similar to the symbol table file. */ /* Reading of extended file name archives is automatic! */ /* If this flag is left off, all filenames will be truncated */ /* to 14 characters, the System V.3 standard. */ if (DECFlag || Extended == DISTRIBUTE) { fprintf( stderr, "Only one of -i -g -d permitted.\n\n" ); usage( argv[0] ); return 22; } Extended = CENTRAL; break; defcas: default: usage( argv[0] ); state = FINISHED; } #if defined( nooptarg ) k++; #endif } #if !defined( nooptarg ) k = optind; #endif if (k == 1 && state == INITIAL) { state = EXTRACT; Flag = NOFLAGS; } else if (state == EXTRACT && Flag == UNLINK) Flag = NOFLAGS; else if (state == INITIAL && Flag == UNLINK) state = APPEND; else if (state == REPLACE && k >= argc - 1) { fprintf( stderr, "Nothing to REPLACE!\n" ); usage( argv[0] ); state = FINISHED; } } /* Open the archive file properly for each mode and verify */ if (Flag == NOFLAGS) ar_arg = 1; else ar_arg = k; if (Flag == UNLINK) strcpy( activity, "m -" ); if (state == APPEND) { /* Open existing archive for append */ fdesc = open( argv[ar_arg], O_RDWR | O_APPEND ); if (fdesc == -1 && errno == ENOENT) { errno = 0; fprintf( stderr, "Archive not found, creating!\n" ); state = CREATE; } else { out_arc = fdopen( fdesc, "a+" ); state = BUILD; FileStatus = OPEN; } } if (state == EXTRACT || state == LISTING) { fdesc = open( argv[ar_arg], O_RDONLY ); if (fdesc == -1 && errno == ENOENT) { errno = 0; fprintf( stderr, "Archive not found! Cannot " ); if (state == EXTRACT) fprintf( stderr, "extract.\n" ); else fprintf( stderr, "list out.\n" ); state = EXIT; } else { out_arc = fdopen( fdesc, "r" ); FileStatus = OPEN; } } if (state == CREATE) { /* Make File & Initialize Header Block */ out_arc = create_arc( argv[ar_arg] ); if (out_arc == (FILE *)NULL) { state = EXIT; fprintf( stderr, "Cannot create archive file: %s\n", argv[ar_arg] ); } else state = BUILD; } else if (state == REPLACE) { /* Move old archive to temporary name */ temp_name[0] = (char)0; /* NULL target filename creates a temp */ if (renameit( argv[ar_arg], temp_name ) != 0) { state = EXIT; fprintf( stderr, "Cannot rename original archive file! Aborting.\n" ); } if (state != EXIT) { in_arc = fopen( temp_name, "r" ); if (in_arc == (FILE *)NULL) { state = EXIT; fprintf( stderr, "Cannot open original archive file. Aborting.\n" ); unlink( temp_name ); } else { j = ar_verify( in_arc ); switch (j) { case 0: break; case -1: fprintf( stderr, "Cannot read archive file. Aborting.\n" ); renameit( temp_name, argv[ar_arg] ); state = EXIT; break; case -2: fprintf( stderr, "%s not a \"ar\" UNIX archive file. " " Aborting.\n", argv[ar_arg] ); renameit( temp_name, argv[ar_arg] ); state = EXIT; break; case -3: fprintf( stderr, "%s is an object library. " " Object Libraries are not supported.\n", argv[ar_arg] ); renameit( temp_name, argv[ar_arg] ); state = EXIT; break; default: fprintf( stderr, "Unknown error code returned. Aborting.\n" ); renameit( temp_name, argv[ar_arg] ); state = EXIT; break; } } } if (state != EXIT) { /* Make New File & Initialize Header Block */ out_arc = create_arc( argv[ar_arg] ); if (out_arc == (FILE *)NULL) { state = EXIT; fprintf( stderr, "Cannot create new archive file: %s\n", argv[ar_arg] ); unlink( argv[ar_arg] ); renameit( temp_name, argv[ar_arg] ); } } } else if (state != EXIT && state != FINISHED) { j = ar_verify( out_arc ); switch (j) { case 0: /* Header verified */ break; case -1: fprintf( stderr, "Cannot read archive file. Aborting.\n" ); state = EXIT; break; case -2: fprintf( stderr, "%s not a \"ar\" UNIX archive file. Aborting.\n", argv[ar_arg] ); state = EXIT; break; case -3: fprintf( stderr, "%s is an object library. " " Object Libraries are not supported.\n", argv[ar_arg] ); state = EXIT; break; default: fprintf( stderr, "Unknown error code returned. Aborting.\n" ); state = EXIT; break; } } if (state == EXTRACT) { if (read_ar( argc, argv, ar_arg, out_arc )) state = EXIT; } else if (state == LISTING) { if (list_ar( argc, argv, ar_arg, out_arc )) state = EXIT; } else if (state == REPLACE) { if (Extended == CENTRAL) { /* Re-write the string table including the new file names. */ rewrite_strings( out_arc, ar_arg, argc, argv ); } if (repl_ar( argc, argv, ar_arg, out_arc, in_arc ) != 0) { unlink( argv[ar_arg] ); renameit( temp_name, argv[ar_arg] ); state = EXIT; } else unlink( temp_name ); } else if (state == BUILD) { if (Extended == CENTRAL) { /* Re-write the string table including the new file names. */ rewrite_strings( out_arc, ar_arg, argc, argv ); } /* For each file, write file and file header */ i = ar_arg; while (++i < argc) { /* Actually for each argument */ errno = 0; /* Only insert ordinary files. */ if (stat( argv[i], &fstats ) == -1) { switch (errno) { case ENOENT: case ENOTDIR: fprintf( stderr, "File <%s> not found. Ignoring!\n", argv[i] ); break; case ENAMETOOLONG: case EPERM: fprintf( stderr, "File name <%s> is invalid. Ignoring!\n", argv[i] ); break; case ELOOP: fprintf( stderr, "Cannot resolve file <%s>, link loop suspected." " Ignoring!\n", argv[i] ); break; } continue; } else if ((fstats.st_mode & S_IFREG)) { /* Cannot copy archive into itself! */ if (strcmp(argv[i], argv[ar_arg])) { fprintf( stdout, "%s %s\n", activity, argv[i] ); if ((j = write_file(out_arc, (FILE *)NULL, argv[i]))== -1) { fprintf( stderr, "\tError writing to archive! Aborting.\n" ); state = EXIT; break; } else if (j == -2) { fprintf( stderr, "\tError reading file! Aborting.\n" ); state = EXIT; break; } } } else { fprintf( stderr, "File <%s> is not an ordinary file. Ignoring!\n", argv[i] ); continue; } state = FINISHED; } } if (FileStatus == OPEN) { fflush( out_arc ); fclose( out_arc ); } if (state == EXIT) { if (Flag == UNLINK) fprintf( stderr, "\tSuccessfully written files have been " "moved into the archive.\n\n" ); else if (Flag == REPLACE) fprintf( stderr, "\tOriginal archive has been restored.\n" ); return 10; } else return 0; } /* Verify that a named file is a valid portable "ar" format archive file. */ int ar_verify( arch_file ) FILE *arch_file; { char magic[SARMAG + 1], magic2[SARMAG + 1]; ar_hdr header; int ch; strcpy( magic2, ARMAG ); /* Seek to begining of file, if appending, file is positioned at end. */ rewind( arch_file ); /* Check for readability. Read ar archive magic header. */ if (fgets( magic, sizeof magic, arch_file ) == (char *)NULL) return(-1); /* Verify ar magic header. */ if (strcmp( magic, magic2 )) return(-2); /* Verify that the first file is not a symbol table */ fgets( (char *)&header, sizeof header, arch_file ); if (header.ar_name[0] == '/' && header.ar_name[1] == ' ') return(-3); else if (header.ar_name[0] == '/' && header.ar_name[1] == '/') { /* Extended filename string table found. Read the table now and access from memory later. */ TableSize = atoi( header.ar_size ); fprintf( stderr, "Archive contains extended file names. " " Reading name table.\n\n" ); NameTable = (char *)malloc( TableSize + 2 ); memset( NameTable, 0, TableSize + 2 ); fread( NameTable, TableSize, 1, arch_file ); strtok( NameTable, "/\n" ); TableSize += 2; /* Replace slashes and NL with NULLs for later. */ while (strtok( (char *)NULL, "/\n" )); /* Position file after string table pad char if any. */ if (atoi( header.ar_size ) % 2) fseek( arch_file, 1, SEEK_CUR ); } else { /* Reposition file after archive header. Irrelevant on append. */ fseek( arch_file, SARMAG, SEEK_SET ); } /* Should probably verify System V -vs- ULTRIX header format here. */ return(0); } /* Extract files from a portable "ar" format archive file. */ int read_ar( argc, argv, cur_arg, arch_file ) int argc; char **argv; FILE *arch_file; int cur_arg; { FILE *out_file; ar_hdr out_hdr; long byte_count = 0, arch_size, out_size, i; long out_date, offs; unsigned short out_mode; char filename[257], *pos, tempstr[100], *file_list, *InList, DOSname[13]; int next_arg, ch, NFiles, Compare, WriteIt, j, k; struct stat arch_stat; char *p; #if defined(__TURBOC__) struct ftime filetime; struct tm *loctime; #else struct utimbuf filetime; #endif if (argc > cur_arg + 1) { /* Selective extract */ Compare = TRUE; i = NFiles = argc - (cur_arg + 1); next_arg = cur_arg + 1; file_list = (char *)malloc( (NFiles + 1) * 257 ); /* Build a list of files to extract */ while (i) strcpy( &file_list[257 * (NFiles - i--)], argv[next_arg++] ); } else Compare = FALSE; stat( argv[cur_arg], &arch_stat ); arch_size = arch_stat.st_size; byte_count = ftell( arch_file ); do { if (fgets((char *)&out_hdr, sizeof(ar_hdr), arch_file) == (char *)NULL) { /* No more headers. We're finished */ break; } byte_count += strlen( (char *)&out_hdr ); if (read_header( &out_hdr, filename, arch_file, &byte_count ) == -1) break; /* Finished, no more headers. */ if (Compare) InList = (char *)lfind( filename, file_list, &NFiles, 257, strcmp ); if (!Compare || (Compare && InList != (char *)NULL)) { WriteIt = TRUE; # if defined (__MSDOS__) || defined(DOSTEST) /* Make the BEST DOS compatible filename we can! */ memset( DOSname, 0, 13 ); j = 0; for (k=0; k<8; k++) if (filename[k] && filename[k] != '.') DOSname[j++] = filename[k]; else break; DOSname[j++] = '.'; if (filename[k]) { p = strchr( filename, '.' ); if (p++) strncpy( &DOSname[j], p, 3 ); else strncpy( &DOSname[j], &filename[k], 3 ); } out_file = fopen( DOSname, "w" ); # else out_file = fopen( filename, "w" ); # endif if (out_file == (FILE *)NULL) { fprintf( stderr, "Cannot open <%s> for writing. Errno = %d.\n", filename, errno ); exit( errno ); } # if defined (__MSDOS__) || defined (DOSTEST) fprintf( stdout, "x - %s => %s\n", filename, DOSname ); # else fprintf( stdout, "x - %s\n", filename ); # endif } else WriteIt = FALSE; strncpy( tempstr, out_hdr.ar_size, sizeof out_hdr.ar_size ); tempstr[sizeof out_hdr.ar_size] = '\0'; out_size = atol( tempstr ); if (WriteIt) { strncpy( tempstr, out_hdr.ar_date, sizeof out_hdr.ar_date ); tempstr[sizeof out_hdr.ar_date] = '\0'; out_date = atol( tempstr ); #if defined(__TURBOC__) loctime = localtime( &out_date ); filetime.ft_year = loctime->tm_year - 80; filetime.ft_month = loctime->tm_mon; filetime.ft_day = loctime->tm_mday; filetime.ft_hour = loctime->tm_hour; filetime.ft_min = loctime->tm_min; filetime.ft_tsec = loctime->tm_sec / 2; #else filetime.actime = filetime.modtime = out_date; #endif strncpy( tempstr, out_hdr.ar_mode, sizeof out_hdr.ar_mode ); tempstr[sizeof out_hdr.ar_mode] = '\0'; out_mode = (unsigned short)otoi( tempstr ); for (i = 0; i < out_size; i++) { if ((ch = getc( arch_file )) < 0) { fprintf( stderr, "Unexpected End of File found. Error %d.\n", ch ); return(9); } if (putc( ch, out_file ) < 0) { fprintf( stderr, "Error writing output. Error %d.\n", ch ); return(8); } byte_count++; } if (out_size % 2 == 1) { if ((ch = getc( arch_file )) < 0) { fprintf( stderr, "Unexpected End of File found.\n" ); return(-1); } else if (ch != '\n') { fprintf( stderr, "File alignment character expected!\n" ); return(-1); } byte_count++; } #if defined(__TURBOC__) setftime( fileno( out_file ), &filetime ); fclose( out_file ); #else fclose( out_file ); utime( filename, &filetime ); #endif chmod( filename, out_mode ); } else { if (out_size % 2) out_size++; fseek( arch_file, out_size, SEEK_CUR ); byte_count += out_size; } } while (byte_count < arch_size); return(0); } /* List out files from a portable "ar" format archive file. */ int list_ar( argc, argv, cur_arg, arch_file ) int argc; char **argv; FILE *arch_file; int cur_arg; { ar_hdr out_hdr; long byte_count = 0, arch_size, out_size, i, skip_bytes, out_date; int j, k; long offs; char *ch; char filename[257], *pos, tempstr[100], p_mode[10], p_date[100], temp1[7], temp2[7]; struct list { char filename[257], access; } *file_list, *InList, OneItem; int next_arg, NFiles, Compare, WriteIt; struct stat arch_stat; #if defined(__TURBOC__) struct ftime filetime; struct tm *loctime; #else struct utimbuf filetime; #endif if (argc > cur_arg + 1) { /* Selective extract */ Compare = TRUE; i = NFiles = argc - (cur_arg + 1); next_arg = cur_arg + 1; file_list = (struct list *)malloc( (NFiles + 1) * sizeof (struct list) ); /* Build a list of files to list out */ OneItem.access = 0; j = 0; while (i--) { strcpy( OneItem.filename, argv[next_arg++] ); lsearch( &OneItem, (char *)file_list, &j, sizeof (struct list), strcmp ); } } else Compare = FALSE; stat( argv[cur_arg], &arch_stat ); arch_size = arch_stat.st_size; byte_count = ftell( arch_file ); do { if (fgets((char *)&out_hdr, sizeof(ar_hdr), arch_file) == (char *)NULL) { /* No more headers. We're finished */ break; } byte_count += strlen( (char *)&out_hdr ); if (read_header( &out_hdr, filename, arch_file, &byte_count ) == -1) break; /* Finished, no more headers. */ if (Compare) { InList = (struct list *)lfind( filename, (char *)file_list, &NFiles, sizeof (struct list), strcmp ); if (InList != (struct list *)NULL) InList->access++; } if (!Compare || (Compare && InList != (struct list *)NULL)) { WriteIt = TRUE; } else WriteIt = FALSE; strncpy( tempstr, out_hdr.ar_size, sizeof out_hdr.ar_size ); tempstr[sizeof out_hdr.ar_size] = '\0'; out_size = atol( tempstr ); skip_bytes = out_size; if (skip_bytes % 2) skip_bytes++; fseek( arch_file, skip_bytes, SEEK_CUR ); byte_count += skip_bytes; if (WriteIt) { strncpy( tempstr, out_hdr.ar_date, sizeof out_hdr.ar_date ); tempstr[sizeof out_hdr.ar_date] = '\0'; out_date = atol( tempstr ); filetime.actime = filetime.modtime = out_date; p_mode[0] = (char) 0; j = 3; do { switch (out_hdr.ar_mode[j]) { case '7': strcat( p_mode, "rwx" ); break; case '6': strcat( p_mode, "rw-" ); break; case '5': strcat( p_mode, "r-x" ); break; case '4': strcat( p_mode, "r--" ); break; case '3': strcat( p_mode, "-wx" ); break; case '2': strcat( p_mode, "-w-" ); break; case '1': strcat( p_mode, "--x" ); break; case '0': strcat( p_mode, "---" ); break; } } while (++j < 6); strftime( p_date, 100, "%b %d %H:%M %Y", localtime( &out_date ) ); /* cftime( p_date, "%b %e %R %Y", &out_date ); */ strncpy( temp1, out_hdr.ar_uid, 6 ); strncpy( temp2, out_hdr.ar_gid, 6 ); temp1[6] = temp2[6] = (char)0; j = atol( temp1 ); k = atol( temp2 ); ch = strchr( filename, ' ' ); if (ch) *ch = (char)0; fprintf( stdout, "%s %04.1d/%-03.0d %6d %s %s\n", p_mode, j, k, out_size, p_date, filename ); } } while (byte_count < arch_size); for ( j=0; j < NFiles; j++ ) if (file_list[j].access == (char)0) fprintf( stdout, "No listing found for file: %s\n", file_list[j].filename ); return(0); } /* Print Usage information. */ void usage( filename ) char *filename; { fprintf( stdout, "%s\n\n%s", filename, copyright ); fprintf( stdout, attribution ); fprintf( stdout, "\nUsage:\t\t%s [-[qmrxtacelni]] arcname [file1 [file 2 [...]]]\n\n", filename ); fprintf( stdout, " With no flags %s extracts files from a UNIX portable archive." "\n\n", filename ); fprintf( stdout, "\tUNIX \"ar\" compatible flags (keys), as follows:\n" ); fprintf( stdout, "\t-q\tQuick append. Add file to end.\n\t\t(This function will " "include duplicate copies of files.)\n" ); fprintf( stdout, "\t-m\tMove file to archive and remove.\n\t\t(This function will " "include duplicate copies of files.)\n" ); fprintf( stdout, "\t-r\tReplace file. Replace file in archive with another version." "\n\t\t(Current version replaces all occurances if the file is " "\n\t\tmultiply contained in the archive.)\n" ); fprintf( stdout, "\t-x\tExtract files.\n" ); fprintf( stdout, "\t-t\tList out contents of archive.\n" ); fprintf( stdout, "\n\tAdditional (non-compatible) flags available:\n" ); fprintf( stdout, "\t-a\tAppend file. Synonym for -q.\n" ); fprintf( stdout, "\t-c\tCreate archive. CREATE a NEW archive or OVERWRITE an " "existing \n\t\tone! " ); fprintf( stdout, "(q, m, a, & r will also CREATE an archive if none exists.)\n" ); fprintf( stdout, "\t-d\tWrite archive with D.E.C. compatible header. " "DEC erroneously\n\t\tdrops the terminating slash at the end of " "the file name.\n\t\tWhile DEC format supports long file names, " "and ar2 can list and\n\t\textract such archives, ar2 will " "truncate long file names it\n\t\tadds to an archive with -d.\n" ); fprintf( stdout, "\t-e\tExtract files. Synonym for -x.\n" ); fprintf( stdout, "\t-g\tWrite archive with Centralized Extended Name table.\n\t\t" "Permits names longer than 14 characters. (DG/UX ar compatible)" "\n\t\tDefault: Truncate names to 14 chars. Extended names \n\t\t" "cannot be appended to an existing archive in this format.\n" ); fprintf( stdout, "\t-l\tList out files. Synonym for -t.\n" ); fprintf( stdout, "\t-i\tWrite archive with Distributed Extended Name table.\n\t\t" "Permits names longer than 14 characters. This format extended" "\n\t\tname table may be appended to.\n" ); fprintf( stdout, " DEC, DG/UX, and ar2 Extended format archives are recognized " "transparently\nfor listing and extraction. The -d, -g, and -i " "options are provided to create\nand update such archives. Only " "one of these options may be provided.\n" ); putc( '\n', stdout ); } /* Convert from Octal Digit String to unsigned integer. */ unsigned otoi( octal ) char *octal; { int value, len, scale, ch; value = 0; scale = 1; len = strlen( octal ); while (len--) { ch = octal[len-1]; if (isdigit(ch)) { value += (ch - '0') * scale; scale *= 8; } else if (value > 0) break; } return (value); } /* Convert from unsigned short to octal digit string. */ char *itoo ( value, octal, length ) unsigned short value; char *octal; int length; { int index, extent; index = extent = digs_8(value); if (value == 0) octal[--index] = '0'; else { while (value) { octal[--index] = (char)('0' + (value % 8)); value /= 8; } } length -= extent; while (length > 0) octal[extent + --length] = ' '; return octal; } /* Convert from long integer to decimal digit string. */ char *ltoa( value, string, length ) long value; char *string; int length; { int index, extent; index = extent = digs_10(value); if (value == 0) { string[--index] = '0'; } else { while (value) { string[--index] = '0' + (value % 10); value = value / 10; } } length -= extent; while (length > 0) string[extent + --length] = ' '; return string; } /* Report number of Decimal Digits contained in a long integer. */ int digs_10( value ) long value; { int digs; digs = 0; do { digs++; value /= 10; } while ( value ); return digs; } /* Report number of Octal Digits contained in an unsigned short. */ int digs_8( value ) unsigned short value; { int digs; digs = 0; do { digs++; value /= 8; } while ( value ); return digs; } /* Create an "ar" format archive file and write magic string. */ FILE *create_arc( filename ) char *filename; { FILE *out_file; char armag[SARMAG + 1]; strcpy( armag, ARMAG ); out_file = fopen( filename, "w" ); if (out_file == (FILE *)NULL) fprintf( stderr, "Cannot create archive file!\n" ); else fputs( armag, out_file ); return out_file; } /* Add a file to an existing archive. */ int write_file ( out_file, in_file, in_name ) FILE *out_file, *in_file; char in_name[]; { ar_hdr header; char *begin; int i, to_read, was_read, were_written, this_read, this_write, IOpened; struct stat fstats; char buffer[MYBUFSIZE], blanks[21], nl[2]; strcpy( blanks, " " ); strcpy( nl, "\n" ); /* Get file statistics */ stat( in_name, &fstats ); /* Build file header record. */ begin = strrchr( in_name, DIRSEP ); if (begin == (char *)NULL) begin = in_name; else begin++; /* Make certain to truncate filenames to 14 characters unless extended string table has been requested. */ if (strlen( begin ) > (sizeof header.ar_name - 2)) { char *nm; long offs, Found = FALSE; if (Extended == CENTRAL) { nm = NameTable; while (*nm != (char)NULL) { if (strcmp( begin, nm ) == 0) { Found = TRUE; break; } else { nm += strlen( nm ) + 2; } } if (Found) { offs = (long)nm - (long)NameTable; sprintf( header.ar_name, "/%0.1d", offs ); } else { fprintf( stderr, "Cannot append to a central string table archive.\n" ); exit(33); } } else if (Extended == DISTRIBUTE) { ar_hdr hdr; strcpy( hdr.ar_name, "!/! " ); ltoa( fstats.st_mtime, hdr.ar_date, 12 ); strcpy( hdr.ar_uid, "0 " ); strcpy( hdr.ar_gid, "0 " ); strcpy( hdr.ar_mode, "600 " ); sprintf( hdr.ar_size, "%-10.1d", strlen( begin ) + 2 ); strcpy( hdr.ar_fmag, ARFMAG ); fputs( hdr.ar_name, out_file ); fprintf( out_file, "%s/\n", begin ); if (strlen( begin ) % 2 == 1) fputc( '\n', out_file ); strncpy( header.ar_name, begin, sizeof header.ar_name - 2 ); header.ar_name[sizeof header.ar_name - 2] = (char)0; strcat( header.ar_name, "/" ); } else { strncpy( header.ar_name, begin, sizeof header.ar_name - 2 ); header.ar_name[sizeof header.ar_name - 2] = (char)0; if (!DECFlag) strcat( header.ar_name, "/" ); fprintf( stderr, "Warning: Name of file <%s> too long, truncated to <%s>.\n", begin, header.ar_name ); } } else { strcpy( header.ar_name, begin ); if (!DECFlag) strcat( header.ar_name, "/" ); } i = strlen( header.ar_name ); /* This is tricky. We're writing the null byte to the beginning of the next element and then overwriting it with the next field data. !!! BE CAREFUL WHEN MODIFYING THIS CODE !!! Do not re-order lines. */ strncpy( &(header.ar_name[i]), blanks, (16 - i) ); ltoa( fstats.st_mtime, header.ar_date, 12 ); ltoa( (long)fstats.st_uid, header.ar_uid, 6 ); ltoa( (long)fstats.st_gid, header.ar_gid, 6 ); itoo( fstats.st_mode, header.ar_mode, 8 ); ltoa( fstats.st_size, header.ar_size, 10 ); strcpy( header.ar_fmag, ARFMAG ); /* Write file header record. */ fputs( header.ar_name, out_file ); /* Open input file */ if (in_file == (FILE *)NULL) { in_file = fopen( in_name, "r" ); if (in_file == (FILE *)NULL) { fprintf( stderr, "Cannot open <%s> for reading. Errno = %d.\n", in_name, errno ); exit( errno ); } IOpened = TRUE; } else IOpened = FALSE; /* Copy file into archive */ to_read = fstats.st_size; this_read = this_write = was_read = were_written = 0; if (to_read > 0) /* Don't bother to read zero length files */ do { was_read += this_read = fread( buffer, 1, MYBUFSIZE, in_file ); were_written += this_write = fwrite( buffer, 1, this_read, out_file ); if (this_write <= 0) break; if (this_read <= 0) break; } while (to_read > was_read); if ((to_read % 2) == 1 && this_read >= 0 && this_write >= 0) this_write = fwrite( nl, 1, 1, out_file ); if (IOpened) fclose( in_file ); fflush( out_file ); if (Flag == UNLINK && this_read >= 0 && this_write >= 0) unlink( in_name ); if (this_read >= 0 && this_write >= 0 && to_read == was_read && to_read == were_written) return were_written; else if (this_write < 0 || to_read > were_written) return -1; else /* (this_read < 0 || to_read > was_read) */ return -2; } /* Replace files from a portable "ar" format archive file with newer(?) ones. */ int repl_ar( argc, argv, cur_arg, new_arch, arch_file ) int argc; char **argv; FILE *arch_file, *new_arch; int cur_arg; { FILE *in_file; ar_hdr out_hdr, sav_hdr; long byte_count = 0, arch_size, out_size, i, write_count = 0, to_read, this_read, block_size; int j, err; char filename[257], *pos, tempstr[100], *block; struct list { char filename[257]; char access; char pathname[1025]; } *file_list, *InList, OneItem; int next_arg, ch, NFiles, Compare, ReplaceIt; struct stat arch_stat, fstats; if (argc > cur_arg + 1) { /* Selective replace */ Compare = TRUE; i = NFiles = argc - (cur_arg + 1); next_arg = cur_arg + 1; file_list = (struct list *)malloc( (NFiles + 1) * sizeof (struct list) ); /* Build a list of files to replace. */ OneItem.access = 0; j = 0; /* New code to allow full paths on command line. */ while (i--) { stat( argv[next_arg], &fstats ); /* Only process ordinary files. */ if (fstats.st_mode && S_IFREG) { strcpy( OneItem.pathname, argv[next_arg++] ); if ((pos = strrchr( OneItem.pathname, DIRSEP )) == (char *)NULL) strcpy( OneItem.filename, OneItem.pathname ); else strcpy( OneItem.filename, ++pos ); lsearch( &OneItem, (char *)file_list, &j, sizeof (struct list), strcmp ); } else fprintf( stderr, "File <%s> is not an ordinary file. Ignoring!\n", argv[next_arg++] ); } } else Compare = FALSE; NFiles = j; fstat( arch_file->_file, &arch_stat ); arch_size = arch_stat.st_size; byte_count = ftell( arch_file ); do { if (fgets((char *)&out_hdr, sizeof(ar_hdr), arch_file) == (char *)NULL) { /* No more headers. We're finished */ break; } byte_count += strlen( (char *)&out_hdr ); /* Save Header in case it is a Name Extension Header */ sav_hdr = out_hdr; if (read_header( &out_hdr, filename, arch_file, &byte_count ) == -1) break; /* Finished, no more headers. */ if (Compare) { InList = (struct list *)lfind( filename, (char *)file_list, &NFiles, sizeof (struct list), strcmp ); if (InList != (struct list *)NULL) InList->access++; } if (Compare && InList != (struct list *)NULL) { if ((in_file = fopen( InList->pathname, "r" )) != (FILE *)NULL) { ReplaceIt = TRUE; } else { fprintf( stdout, "%s not found\n" ); ReplaceIt = FALSE; } } else /* Freshen code would go here */ ReplaceIt = FALSE; strncpy( tempstr, out_hdr.ar_size, sizeof out_hdr.ar_size ); tempstr[sizeof out_hdr.ar_size] = '\0'; out_size = atol( tempstr ); if (ReplaceIt) { /* Replace file with "newer" version, */ /* skip over file in the old archive */ fprintf( stdout, "r - %s\n", filename ); if (out_size % 2) out_size++; fseek( arch_file, out_size, SEEK_CUR ); byte_count += out_size; /* Write the new file to the new archive */ if ((j = write_file( new_arch, in_file, filename )) == -1) { fprintf( stderr, "\tError writing to archive! Aborting.\n" ); return(11); } else if (j == -2) { fprintf( stderr, "\tError reading file! Aborting.\n" ); return(12); } write_count += j; fclose( in_file ); } else { /* Copy file from old archive */ /* First re-write Extended name entry if needed. */ if (strlen( filename ) > (sizeof out_hdr.ar_name - 2)) { if (fputs( (char *)&sav_hdr, new_arch ) == EOF) { fprintf( stderr, "Error copying archive to new archive.\n" ); return(10); } fprintf( new_arch, "%s/\n", filename ); if (strlen( filename ) % 2 == 1) putc( '\n', new_arch ); } /* Now re-write file header */ if (fputs( (char *)&out_hdr, new_arch ) == EOF) { fprintf( stderr, "Error copying archive to new archive.\n" ); return(10); } write_count += (sizeof (ar_hdr) - 1); if (out_size % 2) out_size++; if (out_size > 0) { block_size = (out_size > 65535) ? 65535 : out_size; to_read = out_size; block = malloc( block_size ); while (to_read) { if ((this_read = fread(block, 1, block_size, arch_file)) != block_size) { fprintf( stderr, "Unexpected End of File found. Error %d.\n", ch ); return(9); } if (fwrite( block, 1, block_size, new_arch) != block_size) { fprintf( stderr, "Error writing output. Error %d.\n", err ); return(8); } to_read -= this_read; } free( block ); } byte_count += out_size; write_count += out_size; } } while (byte_count < arch_size); for ( j=0; j < NFiles; j++ ) if (file_list[j].access == (char)0) fprintf( stdout, "File: %s not in archive.\n", file_list[j].filename ); return(0); } /* Portably rename a file - DOS Rename system call, */ /* UNIX link to new name and unlink the old */ int renameit( oldname, newname ) char *oldname, *newname; { #if defined( __MSDOS__ ) /* put rename code for DOS here */ strcpy( newname, oldname ); if (newname[strlen(newname)-1] != '_') newname[strlen(newname)-1] = '_'; else newname[strlen(newname)-1] = '!'; rename( oldname, newname ); #else char *str_ptr, *str_ptr2; if (newname == (char *)NULL) { newname = (char *)malloc( 1025 ); newname[0] = (char)0; } if (strlen(newname) == 0) { /* form a temporary filename */ strcpy( newname, oldname ); if ((str_ptr = strrchr( newname, DIRSEP )) == (char *)NULL) str_ptr = newname; /* get just path to archive file */ else str_ptr++; ltoa( getpid(), str_ptr, 10 ); /* append PID to path */ str_ptr2 = strchr( str_ptr, ' ' ); /* find end of PID */ if (str_ptr2 != (char *)NULL) *str_ptr2 = (char)0; } if (link( oldname, newname ) == -1) return -1; if (unlink( oldname ) == -1) return -2; #endif return 0; } void rewrite_strings( FILE *out, int ar_arg, int argc, char **argv ) { int i = ar_arg, Found = FALSE, oldsize; ar_hdr hdr; char *nm; if (NameTable == (char *)NULL) { NameTable = (char *)malloc( 2 ); memset( NameTable, (char)0, 2 ); TableSize = 2; } while (++i < argc) { if (strlen( argv[i] ) > (sizeof hdr.ar_name - 2)) { oldsize = TableSize; Found = FALSE; nm = NameTable; while (*nm != (char)NULL) { if (strcmp( argv[i], nm ) == 0) { Found = TRUE; break; } else { nm += strlen( nm ) + 2; } } if (!Found) { TableSize += strlen( argv[i] ) + 2; NameTable = (char *)realloc( NameTable, TableSize ); strcpy( &NameTable[oldsize - 2], argv[i] ); memset( &NameTable[TableSize - 4], (char)0, 4 ); } } } if (TableSize > 2) { strcpy( hdr.ar_name, "// " ); ltoa( time( NULL ), hdr.ar_date, 12 ); sprintf( hdr.ar_uid, "%-6.1d", 0 ); sprintf( hdr.ar_gid, "%-6.1d", 0 ); sprintf( hdr.ar_mode, "%-8.1d", 600 ); sprintf( hdr.ar_size, "%-10.1d", TableSize - 2 ); strcpy( hdr.ar_fmag, ARFMAG ); fputs( (char *)&hdr, out ); if (TableSize % 2) oldsize = TableSize - 1; else oldsize = TableSize - 2; /* Copy name table and put the "/\n" name terminators back in the copy before writing it out. */ nm = (char *)malloc( TableSize ); memcpy( nm, NameTable, TableSize ); for (i = 0; i < TableSize - 2; i++) { if (nm[i] == (char)0) { nm[i++] = '/'; nm[i] = '\n'; } } fwrite( nm, oldsize, 1, out ); } free( nm ); } int read_header ( ar_hdr *out_hdr, char *filename, FILE *arch_file, long *byte_count ) { int offs, name_pos, nlen; char *pos; /* Handle distributed string table. */ if (out_hdr->ar_name[1] == '/' && out_hdr->ar_name[2] == '!') { fgets( filename, 257, arch_file ); if ((atoi(out_hdr->ar_size ) % 2) == 1) getc( arch_file ); /* Read actual file's header. */ if (fgets((char *)out_hdr, sizeof(ar_hdr), arch_file) == (char *)NULL) { /* No more headers. We're finished */ return -1; } *byte_count += strlen( (char *)out_hdr ); # if defined( DIAG ) if (out_hdr->ar_name[0] != '/' || out_hdr->ar_name[1] != '?') { fprintf( stderr, "Distributed Name Table Entry found with no " "related file! Ignoring." ); strncpy( filename, out_hdr->ar_name, sizeof out_hdr->ar_name ); } # endif } else { strncpy( filename, out_hdr->ar_name, sizeof out_hdr->ar_name ); filename[ sizeof out_hdr->ar_name ] = (char)0; } pos = strrchr( filename, '/' ); if (pos != (char *)NULL) *pos = (char)0; else { /* Handle DEC style archives with no terminating slash. */ /* Assume no spaces in filenames. Assume long names in place */ /* Assuming 2 trailing spaces after filename before date string */ /* If not adjust " - 3" in "offs = " below. */ pos = strchr( out_hdr->ar_name, ' ' ); *pos = (char)0; strcpy( filename, out_hdr->ar_name ); *pos = ' '; /* Put the space back for repl_ar(). */ if (strlen( filename ) > 14 ) { /* Truncate filename to 14 chars. */ out_hdr->ar_name[14] = out_hdr->ar_name[15] = ' '; /* Reread rest of header! */ offs = -1 * (sizeof(ar_hdr) - strlen( filename ) - 3); fseek( arch_file, offs, SEEK_CUR ); fgets( (char *)out_hdr->ar_date, (sizeof (ar_hdr) - sizeof out_hdr->ar_name), arch_file ); } } /* Handling for extended names with Central String Table. */ if (strlen( filename ) == 0 && NameTable != (char *)NULL) { name_pos = atoi( ++pos ); strcpy( filename, &NameTable[name_pos] ); } return 0; } MKSHAR_EOF echo x - ar2.mk cat >ar2.mk <<'MKSHAR_EOF' VPATH=::../bin:RCS CC=gcc SHELL=/usr/bin/ksh CFLAGS= -fforce-addr -fforce-mem -fdelayed-branch -fschedule-insns2 \ -fexpensive-optimizations -fschedule-insns -frerun-cse-after-loop \ -fthread-jumps ar2: ar2.c make -f ar2.mk compilear make -f ar2.mk ar2.c,v compilear: @if [ -z "${DFLAG}" ]; \ then \ echo $(CC) ${CFLAGS} -oar2 ar2.c;\ $(CC) ${CFLAGS} -oar2 ar2.c;\ else \ echo $(CC) $(DFLAG) -oar2 ar2.c;\ $(CC) $(DFLAG) -oar2 ar2.c;\ fi ar2.c: RCS/ar2.c,v -co $@ ar2.c,v: ar2.c -ci -u $@ MKSHAR_EOF