Path: tut!sunic!mcsun!hp4nl!fwi.uva.nl!croes
From: croes@fwi.uva.nl (Felix A. Croes)
Newsgroups: comp.os.minix
Subject: ld: ar.c
Keywords: ld, ar
Message-ID: <241@fwi.uva.nl>
Date: 6 Nov 89 14:16:10 GMT
Sender: news@fwi.uva.nl
Reply-To: croes@fwi.uva.nl (Felix A. Croes)
Organization: The Courts of Chaos
Lines: 1041

/*
 * ar - archiver for Minix
 *
 * Author:	Felix A. Croes (croes@fwi.uva.nl)
 * Version:	1.0.1
 * Date:	29/09/89
 *
 */

char prog[] = "@(#) Minix archiver v1.0.1, copyright 1989 Felix A. Croes";

/*==========================================================================*
 *			#include's, #define's				    *
 *==========================================================================*/

# include <stdio.h>
# include <ar.h>
# include <out.h>
# include <sys/types.h>
# include <sys/stat.h>

# define TRUE		1
# define FALSE		0
# define FNAMELEN	14

# ifdef ATARI_ST
#  define LARGEST_STRUCT_SIZE	sizeof(struct ar_hdr)
# endif
# ifdef i8088
#  define LARGEST_STRUCT_SIZE	sizeof(struct ar_hdr)
# endif

# ifndef MEMSIZ
#  define MEMSIZ	(8*1024)	/* < MAX_INT */
# endif

# ifndef RANLIBSZ
#  define RANLIBSZ	1024
# endif
# define RLXDEFSZ	(8*RANLIBSZ)

# define align(x)	(((x)+1)&~1)
# define get2301	get0123
# define get2301mem	get0123mem
# define put2301	put0123
# define put2301mem	put0123mem

/*==========================================================================*
 *				global stuff				    *
 *==========================================================================*/

extern char *malloc(), *strcpy(), *strncpy();

char *command, *archive, *tmpfile;
long archive_size;

typedef char bool;

bool	addflag, creatflag, deleteflag, replaceflag,
	listflag, extractflag, oldtimeflag, updateflag,
	verboseflag, localflag, readflag;

/*
 * mem is used by copy() as a buffer for copying between files, and by
 * addranlib() to load the symbol table in. If the object file is small enough,
 * addranlib() loads it completely in mem and sets in_mem to show that copy
 * doesn't have to load it again.
 */
char mem[MEMSIZ];
int in_mem;		/* no. of valid bytes in mem */

void addranlib();	/* forward */

char SYMDEF[] = "__.SYMDEF";
char HEAD[] = SF_HEAD;
char NAME[] = SF_NAME;
char ARCH[] = "2211222";	/* really "41124" */
char XDEF[] = "44";
char SHORT[] = "2";
char LONG[] = "4";

# define MAGIC	SHORT

/*
 * a fatal error has occurred: output message, cleanup & exit
 */
void fatal(format, arg)
char *format, *arg;
{
	fprintf(stderr, "%s: ", command);
	fprintf(stderr, format, arg);
	putc('\n', stderr);
	if (tmpfile)
		unlink(tmpfile);
	exit(1);
}

void mem_error()
{
	fatal("out of memory");
}

/*==========================================================================*
 *				io package				    *
 *==========================================================================*/

/* the MFILE structure */

typedef struct {
	int _fd;
	int _count;
	long _posn;
	char *_buf;
	char *_ptr;
	/*
	 * mcreat() and mopen() are always called with a pointer to some
	 * data that is not destroyed before closing the file. Therefore the
	 * pointer can be kept in this structure, for better error messages.
	 */
	char *_file;
} MFILE;

void read_error(fp)
MFILE *fp;
{
	fatal("%s: read error", fp->_file);
}

void write_error(fp)
MFILE *fp;
{
	fatal("%s: write error", fp->_file);
}

# define mtell(fp)	(fp->_posn)

/*
 * create a file. return NULL if could not create
 */
MFILE *mcreat(file)
char *file;
{
	register MFILE *fp;
	int fd;

	/* create file */
	if ((fd = creat(file, 0644)) < 0)
		return NULL;

	/* allocate memory */
	fp = (MFILE*) malloc(sizeof(MFILE) + BUFSIZ);
	if (fp == NULL) mem_error();

	/* fill in MFILE structure */
	fp->_fd = fd;
	fp->_count = 0;
	fp->_posn = 0L;
	fp->_ptr = fp->_buf = (char*) (fp+1);
	fp->_file = file;

	return fp;
}

/*
 * open a file. return NULL if could not open
 */
MFILE *mopen(file)
char *file;
{
	register MFILE *fp;
	int fd;

	/* open file */
	if ((fd = open(file, 0)) < 0)
		return NULL;

	/* allocate memory */
	fp = (MFILE*) malloc(sizeof(MFILE) + BUFSIZ);
	if (fp == NULL) mem_error();

	/* fill in MFILE structure */
	fp->_fd = fd;
	fp->_count = 0;
	fp->_posn = 0L;
	fp->_buf = (char*) (fp+1);	/* no need to set fp->_ptr here */
	fp->_file = file;

	return fp;
}

/*
 * read bytes. return 0 if no bytes could be read (EOF).
 */
int mread(fp, buf, n)
register MFILE *fp;
register char *buf;
register int n;
{
	register int needed;

	do {
	    /* first read from buffer */
	    if (fp->_count) {
		needed = (fp->_count > n) ? n : fp->_count;
		bcopy(fp->_ptr, buf, needed);
		buf += needed;
		n -= needed;
		fp->_count -= needed;
		fp->_ptr += needed;
		fp->_posn += needed;
	    }
	    /* then read directly */
	    if (n) {
		needed = n - (n % BUFSIZ);
		if (needed) {
		    if (read(fp->_fd, buf, needed) != needed)
			read_error(fp);
		    buf += needed;
		    n -= needed;
		    fp->_posn += needed;
		}

		/* then fill buffer and try again */
		if (n) {
		    if ((needed = read(fp->_fd, fp->_buf, BUFSIZ)) == 0)
			/* only at this point EOF is allowed */
			return 0;	/* EOF */
		    if (needed < n) read_error(fp);
		    fp->_ptr = fp->_buf;
		    fp->_count = needed;
		}
	    }
	} while (n);

	return 1;	/* success */
}

/*
 * write bytes to fp. No errors allowed
 */
void mwrite(fp, buf, n)
register MFILE *fp;
register char *buf;
register int n;
{
	register int left;

	do {
		/* first fill buffer */
		left = BUFSIZ - fp->_count;
		if (left > n) left = n;
		bcopy(buf, fp->_ptr, left);
		buf += left;
		n -= left;
		fp->_count += left;
		fp->_posn += left;
		fp->_ptr += left;

		/* empty the buffer */
		if (fp->_count >= BUFSIZ) {
			if (write(fp->_fd, fp->_buf, fp->_count) < 0)
				write_error(fp);
			fp->_count = 0;
			fp->_ptr = fp->_buf;

			/* then write directly */
			left = n - (n % BUFSIZ);
			if (left) {
				if (write(fp->_fd, buf, left) < 0)
					write_error(fp);
				buf += left;
				n -= left;
				fp->_posn += left;
			}
		}
	} while (n);
}

/*
 * mseek. how can only be 0 or 1, fp must be a file opened for reading
 */
void mseek(fp, offset, how)
register MFILE *fp;
register long offset;
int how;
{
	long lseek();

	if (how == 0) offset -= fp->_posn;
	fp->_posn += offset;
	if (fp->_count != 0 &&
	    offset >= fp->_buf - fp->_ptr && offset < fp->_count) {
		fp->_count -= offset;
		fp->_ptr += offset;
	} else {
		if (lseek(fp->_fd, fp->_posn, 0) < 0)
			read_error(fp);
		fp->_count = 0;
	}
}

/*
 * close a file opened for reading (or writing, when called from mwclose)
 */
void mrclose(fp)
MFILE *fp;
{
	close(fp->_fd);
	free(fp);
}

/*
 * flush buffers and close a file (opened for writing).
 */
void mwclose(fp)
register MFILE *fp;
{
	if (fp->_count && write(fp->_fd, fp->_buf, fp->_count) < 0)
		write_error(fp);
	mrclose(fp);
}


/*==========================================================================*
 *			8086 byte order conversion			    *
 *==========================================================================*/

/*
 * read structure in reverse byte order from memory
 */
void get0123mem(ad, buf, sizes)
register char *ad, *buf, *sizes;
{
	register int size;

	while (size = *sizes++) {
		size -= '0';
		buf += size;
		switch (size) {
		case 4:
			*--buf = *ad++;
			*--buf = *ad++;
		case 2:
			*--buf = *ad++;
		case 1:
			*--buf = *ad++;
		}
		buf += size;
	}
}

/*
 * write structure in reverse byte order to memory
 */
void put0123mem(buf, ad, sizes)
register char *buf, *ad, *sizes;
{
	register int size;

	while (size = *sizes++) {
		size -= '0';
		buf += size;
		switch (size) {
		case 4:
			*ad++ = *--buf;
			*ad++ = *--buf;
		case 2:
			*ad++ = *--buf;
		case 1:
			*ad++ = *--buf;
		}
		buf += size;
	}
}


/*
 * read structure in reverse byte order from file
 */
void get0123(fp, buf, size, sizes)
MFILE *fp;
char *buf, *sizes;
int size;
{
	char convbuf[LARGEST_STRUCT_SIZE];

	if (mread(fp, convbuf, size) == 0) read_error(fp);
	get0123mem(convbuf, buf, sizes);
}

/*
 * write structure in reverse byte order to file
 */
void put0123(fp, buf, size, sizes)
MFILE *fp;
char *buf, *sizes;
int size;
{
	char convbuf[LARGEST_STRUCT_SIZE];

	put0123mem(buf, convbuf, sizes);
	mwrite(fp, convbuf, size);
}


/* some utilities */

/*
 * return non-directory part of filename
 */
char *nodir(file)
char *file;
{
	register char *p;
	char *rindex();

	p = rindex(file, '/');
	return (p)? p+1 : file;
}

/*
 * copy (part of) a file from sfp to dfp
 */
void copy(sfp, dfp, size)
register MFILE *sfp, *dfp;
register long size;
{
	/*
	 * if in_mem is non-zero, then the object file is already loaded
	 * there - no need to load it again. Otherwise, read it from sfp
	 * and write it to dfp.
	 * NOTE: depending on in_mem, the seek file posn is directly after
	 * the header (in_mem == 0) or at the end (in_mem != 0) on entry
	 * of this function. Afterwards it is always at the end.
	 */
	if (!in_mem) {		/* nothing in mem */
		while (size > MEMSIZ) {
			if (mread(sfp, mem, MEMSIZ) == 0) read_error(sfp);
			mwrite(dfp, mem, MEMSIZ);
			size -= MEMSIZ;
		}
		if (mread(sfp, mem, (int)size) == 0) read_error(sfp);
	} else
		in_mem = 0;	/* mem contains file */

	mwrite(dfp, mem, (int)size);
}

/*==========================================================================*
 *			archive manipulating functions			    *
 *==========================================================================*/

/*
 * All these different byte orders are very confusing... In object files, the
 * order is 0123 for longs. In archive headers, the order is 2301!
 * Why can't it just be 3210? (Sigh)
 */

/*
 * read an archive header
 */
bool readheader(hdr, fp)
struct ar_hdr *hdr;
register MFILE *fp;
{
	if (mread(fp, hdr->ar_name, FNAMELEN) == 0)
		return FALSE;	/* EOF */
	get2301(fp, &hdr->ar_date, sizeof(*hdr)-FNAMELEN, ARCH);
	return TRUE;
}

/*
 * write an archive header
 */
void writeheader(hdr, fp)
struct ar_hdr *hdr;
register MFILE *fp;
{
	mwrite(fp, hdr->ar_name, FNAMELEN);
	put2301(fp, &hdr->ar_date, sizeof(*hdr)-FNAMELEN, ARCH);
}

/*
 * create an archive
 */
MFILE *acreat(file)
char *file;
{
	register MFILE *fp;
	short magic = ARMAG;

	fp = mcreat(file);
	if (fp == NULL)
		fatal("cannot create %s\n", file);
	put0123(fp, &magic, sizeof(magic), MAGIC); /* insert magic number */
	return fp;
}

/*
 * open an archive for reading. if is does not exist and (create == TRUE),
 * issue a 'creating ...' message if (message == TRUE).
 * if (create == FALSE) => error
 */
MFILE *aopen(file, create, message)
char *file;
bool create, message;
{
	register MFILE *fp;
	short magic;
	struct ar_hdr hdr;

	fp = mopen(file);
	if (fp == NULL)
		if (create) {
			if (message)
				printf("%s: creating %s\n", command, file);
		} else fatal("cannot open %s", file);
	else {
		get0123(fp, &magic, sizeof(magic), MAGIC);
		if (magic != ARMAG)
			fatal("%s: not in ar format", file);

		/* skip __.SYMDEF header */
		if (readheader(&hdr, fp))
			mseek(fp, (strncmp(SYMDEF, hdr.ar_name, FNAMELEN) == 0)
					? hdr.ar_size	/* always even */
					: (long) -sizeof(hdr),
				1);
	}
	return fp;
}

/*
 * copy a file from (archive or file) sfp to (archive) dfp
 */
void copyfile(hdr, sfp, dfp)
register struct ar_hdr *hdr;
register MFILE *sfp, *dfp;
{
	if (archive_size & 1) {		/* align */
		archive_size++;
		mwrite(dfp, "", 1);	/* there is a \0 hiding in there */
	}
	writeheader(hdr, dfp);
	addranlib(hdr, mtell(dfp), sfp);
	copy(sfp, dfp, hdr->ar_size);
	archive_size += sizeof(*hdr) + hdr->ar_size;
}

/*
 * skip an archive entry
 */
void skip(hdr, fp)
struct ar_hdr *hdr;
MFILE *fp;
{
	mseek(fp, hdr->ar_size, 1);
}

/*
 * add a file to an archive. return TRUE if it was added, FALSE otherwise
 */
bool addfile(file, date, afp)
char *file;
long date;	/* only add if more recent than date */
MFILE *afp;
{
	struct ar_hdr hdr;
	struct stat buf;
	register MFILE *fp;

	if (stat(file, &buf) >= 0 &&
	    (unsigned long)buf.st_mtime > (unsigned long)date) {

		/* it exists and is up-to-date: copy info to hdr */
		strncpy(hdr.ar_name, nodir(file), FNAMELEN);
		hdr.ar_date = buf.st_mtime;
		hdr.ar_uid = buf.st_uid;
		hdr.ar_gid = buf.st_gid;
		hdr.ar_mode = buf.st_mode;
		hdr.ar_size = buf.st_size;
		fp = mopen(file);
		if (fp) {
			/* can be read: put it in the archive */
			copyfile(&hdr, fp, afp);
			mwclose(fp);
			return TRUE;
		}
	}
	/* not a fatal error */
	return FALSE;
}

/*
 * extract a file. return (file == extracted)
 */
bool extract(hdr, file, oldtime, afp)
register struct ar_hdr *hdr;
register char *file;
bool oldtime;
MFILE *afp;
{
	register MFILE *fp;

	fp = mcreat(file);
	if (fp == NULL) {
		/* not a fatal error */
		fprintf(stderr, "cannot extract %s\n", file);
		skip(hdr, afp);
		return FALSE;
	} else {
		int uid;

		copy(afp, fp, hdr->ar_size);
		mwclose(fp);
		chmod(file, hdr->ar_mode);
		if (oldtime && ((uid = getuid()) == 0 || uid == hdr->ar_uid)) {
			time_t timep[2];

			timep[1] = timep[0] = hdr->ar_date;
			utime(file, timep);
			if (uid == 0) {
				chown(file, hdr->ar_uid, hdr->ar_gid);
			}
		}
		return TRUE;
	}
}

/*
 * list an archive entry
 */
void list(hdr, verbose)
register struct ar_hdr *hdr;
bool verbose;
{
	char modestring[10], *ctime();
	register char *p;
	register int shift;

	if (verbose) {
		/* construct modestring */
		strcpy(modestring, "rwxrwxrwx");
		for (p = modestring, shift = 0400; shift; p++, shift >>= 1) {
			if (!(hdr->ar_mode & shift)) *p = '-';
		}
		printf("%s %3d/%-3d %7ld %.20s %.14s\n",
			modestring,
			hdr->ar_uid, hdr->ar_gid,
			hdr->ar_size,
			ctime(&hdr->ar_date) + 4,
			hdr->ar_name);
	} else {
		printf("%.14s\n", hdr->ar_name);
	}
}

/*==========================================================================*
 *				ranlib functions			    *
 *==========================================================================*/

struct xdef {
	long x_name;
	long x_foff;
} xdef[RANLIBSZ];			/* externally defined symbols */
long xdsize;
char xdstrings[RLXDEFSZ], *xdp = xdstrings;	/* xdef string table */

/*
 * add external symbols from object file to ranlib header
 */
void addranlib(hdr, offset, fp)
struct ar_hdr *hdr;
long offset;
MFILE *fp;
{
	struct outhead header;
	struct outname name, *names;
	char *strings;
	long posn;

	/* could check here if name ends in .o */

	if (hdr->ar_size < sizeof(header)) return;

	posn = mtell(fp);
	if (hdr->ar_size <= MEMSIZ) {
		/*
		 * load whole object file in memory
		 */
		in_mem = hdr->ar_size;	/* set in_mem to show mem holds something */
		mread(fp, mem, in_mem);

		get0123mem(mem, &header, HEAD);
		/*
		 * return immediately if not object file
		 */
		if (BADMAGIC(header)) return;

		names = (struct outname*)(mem + OFF_NAME(header));
	} else {
		/*
		 * load only header and symbol table in memory
		 */
		get0123(fp, &header, SZ_HEAD, HEAD);

		/*
		 * return immediately if not object file
		 */
		if (BADMAGIC(header)) {
			mseek(fp, posn, 0);
			return;
		}

		if (header.oh_nname*SZ_NAME + header.oh_nchar > MEMSIZ)
			mem_error();
		mseek(fp, OFF_NAME(header) - SZ_HEAD, 1);
		if (mread(fp, mem, (int)(header.oh_nname*SZ_NAME + header.oh_nchar)) == 0)
			read_error(fp);
		names = (struct outname*)mem;
		mseek(fp, posn, 0);
	}
	strings = (char *)(names + header.oh_nname) - OFF_CHAR(header);

	/*
	 * Process xdefs.
	 * Offset is the offset of the object module in the library, the
	 * __.SYMDEF header not taken into account.
	 */
	while (header.oh_nname-- > 0) {
		get0123mem(names++, &name, NAME);
		if ((name.on_type & (S_EXT|S_TYP)) > S_EXT) {
			char *p = strings + name.on_foff;

			if (xdp + strlen(p) + 1 >= &xdstrings[RLXDEFSZ])
				mem_error();

			strcpy(xdp, p);
			xdef[xdsize].x_name = xdp - xdstrings;
			xdef[xdsize++].x_foff = offset;
			xdp += strlen(xdp) + 1;
		}
	}
}

/*
 * put the ranlib header to fp
 */
void putranlib(fp)
register MFILE *fp;
{
	long dsize, time();
	register long tdsize, i;
	register struct xdef *x;
	struct ar_hdr hdr;

	/* dsize = size of xdstring table */
	dsize = align(xdp - xdstrings);
	/* tdsize = size of SYMDEF entry */
	tdsize = 2*sizeof(long) + xdsize*sizeof(struct xdef) + dsize;

	/* output header */
	strncpy(hdr.ar_name, SYMDEF, FNAMELEN);
	hdr.ar_date = time((long*)0)+30*60L;	/* 30 minutes ahead */
	hdr.ar_uid = getuid();
	hdr.ar_gid = getgid();
	hdr.ar_mode = 0444;	/* r--r--r-- */
	hdr.ar_size = tdsize;
	writeheader(&hdr, fp);

	/* output no. of xdefs */
	put0123(fp, &xdsize, sizeof(long), LONG);

	/* output xdefs */
	x = xdef;
	for (i = xdsize; i > 0; --i) {	/* output in 8086 byte order */
		x->x_foff += tdsize;
		put0123(fp, x++, sizeof(struct xdef), XDEF);
	}

	/* output string table size */
	put0123(fp, &dsize, sizeof(long), LONG);

	/* output string table */
	mwrite(fp, xdstrings, (int)dsize);
}

/*==========================================================================*
 *				utilities				    *
 *==========================================================================*/

/*
 * check if a filename is in a list.
 * return filename found, or zero
 */
char *match(file, filelist)
register char *file, *filelist[];
{
	while (*filelist) {
		if (**filelist && strncmp(file, nodir(*filelist), FNAMELEN) == 0)
			return *filelist;
		filelist++;
	}
	return NULL;
}

/*
 * copy one archive to another
 */
void copyfiles(sfp, dfp)
MFILE *sfp, *dfp;
{
	copy(sfp, dfp, archive_size);
}

/*
 * show usage of ar command
 */
void usage()
{
	fprintf(stderr, "usage: %s [adrtxclouv] archive [file] ...\n",
		command);
	exit(2);
}

/*==========================================================================*
 *				MAIN program				    *
 *==========================================================================*/

main(argc, argv)
register int argc;
register char *argv[];
{
	char *options, *file;
	register MFILE *afp, *tfp;
	struct ar_hdr hdr;


	command = *argv++;

	/* process options */
	if (argc < 3) usage();
	options = *argv++;
	if (*options == '-') options++;		/* skip '-' */
	switch (*options++) {
	case 'a':
		addflag = TRUE;		/* add file to archive */
		break;
	case 'd':
		deleteflag = TRUE;	/* delete file */
		break;
	case 'r':
		replaceflag = TRUE;	/* replace file */
		break;
	case 't':
		listflag = TRUE;	/* list archive */
		readflag = TRUE;	/* just reading */
		break;
	case 'x':
		extractflag = TRUE;	/* extract file */
		readflag = TRUE;	/* just reading */
		break;
	default:
		usage();
	}
	while (*options) switch (*options++) {
	/* NEVER MIND if these flags are given more than once */
	case 'c':
		creatflag = TRUE;	/* suppress 'creating ...' message */
		break;
	case 'o':
		oldtimeflag = TRUE;	/* restore time on extraction */
		break;
	case 'u':
		updateflag = TRUE;	/* update - used with r */
		break;
	case 'v':
		verboseflag = TRUE;	/* verbose */
		break;
	case 'l':
		localflag = TRUE;	/* tmpfile in current directory */
		break;
	default:
		usage();
	}

	/* safety check */
	if (addflag || replaceflag || extractflag) {
		register char **a1 = argv, **a2;

		/*
		 * check for duplicates in the argument list
		 */
		while (*a1) {
			a2 = a1;
			while (*++a2) {
				if (strcmp(*a1, *a2) == 0) {
					**a2 = '\0';
					--argc;
				}
				a2++;
			}
			a1++;
		}
	}

	/* sanity check */
	if (((creatflag | localflag) && readflag) ||
	    (updateflag > replaceflag) ||
	    (oldtimeflag > extractflag) ||
	    (!readflag && argc == 3))
		usage();

	/* open archive */
	archive = *argv++;
	afp = aopen(archive, addflag | replaceflag, !creatflag);

	/* do the job */
	if (!readflag) {
		/* CHANGING THE ARCHIVE */
		extern char *mktemp();

		/* make tmparchive */
		tmpfile = mktemp("/tmp/arXXXXXX" + ((localflag) ? 5 : 0));
		tfp = acreat(tmpfile);

		/* so far, no changes */
		readflag = TRUE;

		/* main loop */
		if (afp) {
		  while (readheader(&hdr, afp)) {
		    if ( (file=match(hdr.ar_name, argv)) ) {

			if (replaceflag) {
			    if (addfile(file, (updateflag) ? hdr.ar_date : 0L, tfp)) {
				readflag = FALSE;
				if (verboseflag) printf("r - %s\n", file);
			    } else
				copyfile(&hdr, afp, tfp);
			}

			else if (deleteflag) {
			    readflag = FALSE;
			    if (verboseflag) printf("d - %s\n", file);
			}

			else {	/* addflag - double */
			    printf("%s: already in archive\n", file);
			    copyfile(&hdr, afp, tfp);
			}

			*file = '\0';	/* mark as used */

		    } else {
			/* copy from archive to tmpfile */
			copyfile(&hdr, afp, tfp);
		    }
		    if (hdr.ar_size & 1)	/* align */
			mread(afp, hdr.ar_name, 1);
		  }
		  mrclose(afp);
		}

		/* add files to tmpfile - if nesseccary */
		if (addflag || replaceflag) {
		    register char **ap = argv;

		    do {
			if (**ap && addfile(*ap, 0L, tfp)) {
			    readflag = FALSE;
			    if (verboseflag)
				printf("a - %s\n", *ap);
			    **ap = '\0';	/* mark as used */
			}
		    } while (*++ap);
		}

		if (!readflag) {	/* changes were made */
			/* close and reopen the tmpfile */
			mwclose(tfp);
			tfp = aopen(tmpfile, FALSE, FALSE);

			/* creat new archive */
			afp = acreat(archive);

			/* output new archive */
			putranlib(afp);
			copyfiles(tfp, afp);
			mwclose(afp);
		}
		mrclose(tfp);
		unlink(tmpfile);

	} else {
		/* READING ARCHIVE */

		file = NULL;
		/* main loop */
		while (readheader(&hdr, afp)) {

		    if (argc == 3 || (file=match(hdr.ar_name, argv))) {
			if (extractflag) {
			    static char *ff, buf[FNAMELEN+1];
			    /*
			     * the file name must be \0-terminated, so copy
			     * it to a buffer where this can be done
			     */
			    ff = (file)?
				file :
				strncpy(buf, hdr.ar_name, FNAMELEN);
			    if (extract(&hdr, ff, oldtimeflag, afp) &&
				verboseflag)
			    	printf("x - %s\n", ff);
			} else {
			    list(&hdr, verboseflag);
			    skip(&hdr, afp);
			}
		    } else skip(&hdr, afp);

		    if (file) *file = '\0';	/* mark as used */
		    if (hdr.ar_size & 1)	/* align */
			mread(afp, hdr.ar_name, 1);
		}
		mrclose(afp);
	}
	/* ready */

	/* warnings */
	while (*argv) {
		if (**argv) printf("%s: not found\n", *argv);
		argv++;
	}

	exit(0);
}
--
+--------------------------------------|--------------------------------------+
| "GEM is dead -		       | 	    croes@fwi.uva.nl	      |
|		 long live Minix!"     |    croes%fwi.uva.nl@hp4nl.nluug.nl   |
+--------------------------------------|--------------------------------------+
