[comp.os.minix] V7 ar for MINIX

mwalls@unirot.UUCP (monty walls) (01/03/88)

----------------------------------------------------------------------------

	Enclosed is a complete mostly V7 compatible version of 'ar'.
I wrote this after getting frustrated with the old version of 'ar' while
doing library maintence.  It handles all the V7 options.  Have fun with
it.  
	-Monty Walls

-------------------------------cut-here-------------------------------------
echo x - ar.man
gres '^X' '' > ar.man << '/'
XNAME
X	ar
X
XSYNOPSIS
X	ar 'qmrdtpxvuaibcl' [posname] archive [file] ...
X
XDESCRIPTION
X	'Ar' maintains unix V7 compatible archives.  It allows groups of
X	files to be combined into a single archive file.  It is normally
X	used to maintain object libraries.
X
X	one of: qrqtpmx
X
X	  q: quickly append to the end of the archive file.  'Ar' does
X	     not check for duplicate members.
X
X	  m: move named files. 'Ar' expects 'a', 'b', or 'i' to be
X	     specified also. If 'posname' is not specified 'Ar' will
X	     move named files to the end of the archive.
X
X	  r: replace (append when not in archive). if 'u' is also 
X	     specified then 'Ar' will only replace if new version is newer.
X	     if 'a' or 'b' or 'i' are specified 'posname' is expected and
X	     any new members will be placed either after ('a') or before
X	     ('b' or 'i') 'posname'.
X
X	  d: delete. 'Ar' will delete the name members.
X	  t: print contents of archive
X	  p: print named files
X	  x: extract
X
X	optionally concatencated with one or more of: vuaibcl
X  
X	  l: local temporary file for work instead of /tmp/ar.$$$$$
X	  v: verbose
X	  a: after 'posname'
X	  b: before 'posname'
X	  i: before 'posname'
X	  c: create  (suppresses creation message)
X	  u: replace only if dated later than member in archive
X
X
XFILES
X	/tmp/ar.1.*	Temporaries
X
XBUGS
X	'Ar' will happily add multiple copies of a file to the archive.
X	'Ar' will use the first occurence of a member only for
X	  'x', 'd', 'p', 'm', and 'r'.
X
/
echo x - ar.h
gres '^X' '' > ar.h << '/'
X/* ar.c header file V7 */
X
X#define ARMAG	0177545l
Xstruct ar_hdr {
X	char ar_name[14];
X	long ar_date;		/* not Intel format */
X	char ar_uid;
X	char ar_gid;
X	int ar_mode;
X	long ar_size;		/* not Intel format */
X};
/
echo x - ar.c
gres '^X' '' > ar.c << '/'
X/* ar - archiver		Author: Michiel Huisjes */
X/* V7 upgrade			Author:	Monty Walls */
X
X/*
X * Usage: ar 'key' [posname] archive [file] ...
X *
X *	where 'key' is one of: qrqtpmx
X *
X *	  q: quickly append to the end of the archive file
X *	  m: move named files 
X *	  r: replace (append when not in archive)
X *	  d: delete
X *	  t: print contents of archive
X *	  p: print named files
X *	  x: extract
X *
X *	concatencated with one or more of: vuaibcl
X *  
X *	  l: local temporary file for work instead of /tmp/ar.$$$$$
X *	  v: verbose
X *	  a: after 'posname'
X *	  b: before 'posname'
X *	  i: before 'posname'
X *	  c: create  (suppresses creation message)
X *	  u: replace only if dated later than member in archive
X */
X
X/* mods:
X *	1.2 upgrade.
X *	local directory support	(mrw).
X *	full V7 functionality + complete rewrite (mrw).
X *
X * notes:
X *	pdp11 long format & Intel long format are different.
X */
X
X/* include files */
X#include <stdio.h>
X#include <stat.h>
X#include <signal.h>
X#include <ar.h>
X
X/* macro functions */
X#define FOREVER		(32766)
X#define odd(nr)		(nr & 1)
X#define even(nr)	(odd(nr) ? nr + 1 : nr)
X#define quit(pid,sig)	(kill(pid,sig),sleep(FOREVER))
X#ifndef tell 
X#	define tell(f)	(lseek(f, 0l, 1))
X#endif
X
X/* option switches */
X/* major options */
X#define EXTRACT		0x01
X#define REPLACE		0x02
X#define PRINT		0x04
X#define TABLE		0x08
X#define DELETE		0x10
X#define APPEND		0x20
X#define MOVE		0x40
X
X/* minor options */
X#define BEFORE		0x01
X#define AFTER		0x02
X#define LOCAL		0x01
X#define VERBOSE		0x01
X#define CREATE		0x01
X#define ONLY		0x01
X
X/* mode bits maps */
X#define EXEC_OWNER	00001
X#define EXEC_GROUP	00010
X#define EXEC_ALL	00100
X#define READ_OWNER	00004
X#define READ_GROUP	00040
X#define READ_ALL	00400
X#define WRITE_OWNER	00002
X#define WRITE_GROUP	00020
X#define WRITE_ALL	00200
X#define SET_UID		04000
X#define SET_GID		02000
X
X/* global defines */
X#define BUFFERSIZE	4096
X#define WRITE		2		/* both read & write */
X#define READ		0
X#define MAGICSIZE	sizeof(short)	/* size of magic number in file */
X
X/* option switches */
Xchar verbose = 0;
Xchar local = 0;
Xchar create = 0;
Xchar only = 0;
Xchar major = 0;
Xchar minor = 0;
X
X/* global variables */
Xchar *tmp1;
Xchar *tmp2;
Xchar *progname;
Xchar *posname = NULL;
Xchar *afile;
Xchar buffer[BUFFERSIZE];
Xlong pos_offset = -1;
Xint mypid;
X
X/* keep track of member moves using this struct */
Xstruct mov_list {
X	long pos;
X	struct mov_list *next;
X} *moves = NULL;
X
X/* forward declarations and external references */
Xextern char *malloc();
Xextern char *mktemp(), *rindex();
Xextern int strcmp();
Xextern print_date();
Xextern user_abort(), usage();
Xextern long lseek();
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int ac, opts_seen = 0, rc;
X	char *av;
X	
X	progname = argv[0];		
X	if (argc < 3) 
X		usage();
X
X	for (av = argv[1]; *av; ++av) {	
X		switch (*av) {		/* major option */
X			case 'q':
X				major |= APPEND;
X				++opts_seen;
X				break;
X			case 'r':
X				major |= REPLACE;
X				++opts_seen;
X				break;
X			case 'x':
X				major |= EXTRACT;
X				++opts_seen;
X				break;
X			case 'p':
X				major |= PRINT;
X				++opts_seen;
X				break;
X			case 'm':
X				major |= MOVE;
X				++opts_seen;
X				break;
X			case 'd':
X				major |= DELETE;
X				++opts_seen;
X				break;
X			case 't':
X				major |= TABLE;
X				++opts_seen;
X				break;
X			case 'l':
X				local |= LOCAL;
X				break;
X			case 'a':
X				minor |= AFTER;
X				break;
X			case 'i':
X			case 'b':
X				minor |= BEFORE;
X				break;
X			case 'v':
X				verbose |= VERBOSE;
X				break;
X			case 'c':
X				create |= CREATE;
X				break;
X			case 'u':
X				only |= ONLY;
X				break;
X			default:
X				usage();
X		}
X	}
X
X	if (opts_seen != 1) 
X		usage();
X
X	/* now do edits on options */
X	if (!(major & (REPLACE | MOVE))) {
X		if (minor & (AFTER | BEFORE))
X			usage();
X	}
X	else if (major & MOVE) {
X		if (!(minor & (AFTER | BEFORE)))
X			usage();
X	}
X	else if (only & ONLY)
X		if (!(major & REPLACE))
X			usage();
X	
X	if (local) 
X		tmp1 = mktemp("./ar.1.XXXXXX");
X	else
X		tmp1 = mktemp("/tmp/ar.1.XXXXXX");
X			
X	/* now if minor says AFTER or BEFORE - then get posname */
X	if (minor & (AFTER | BEFORE) && argc >= 4) {
X		posname = argv[2];
X		afile = argv[3];
X		ac = 4;
X	}
X	else {
X		posname = (char *)NULL;
X		afile = argv[2];
X		ac = 3;
X	}
X	
X	/* exit logic consists of doing a kill on my pid to insure that we */
X	/* get the current clean up and exit logic */
X	mypid = getpid();	
X	signal(SIGINT, user_abort);
X	
X	switch (major) {
X		case REPLACE:
X		case DELETE:
X		case MOVE:
X			ar_members(ac, argc, argv);
X			break;
X		case EXTRACT:
X		case TABLE:
X		case PRINT:
X			ar_common(ac, argc, argv);
X			break;
X		case APPEND:
X			append_members(ac, argc, argv);
X			break;
X		default:
X			usage();
X	}
X
X	for (rc = 0; ac < argc; ++ac) {
X		if (*argv[ac] != '\0') {
X			/* no processing done on this name */
X			fprintf(stderr,"Error %s: %s not found in ar\n", progname, argv[ac]);
X			rc = 1;
X		}
X	}
X	fflush(stdout);
X	exit(rc);
X}
X
Xusage()
X{
X	fprintf(stderr,"Usage: %s [qrxdpmt][abivulc] [posname] afile name ... \n",progname);
X	exit(1);
X}
X
Xuser_abort()
X{
X	unlink(tmp1);
X	exit(1);
X}
X
Xinsert_abort()
X{
X	unlink(tmp1);
X	unlink(tmp2);
X	exit(1);
X}
X
Xmwrite(fd, address, bytes)
Xint fd;
Xregister char *address;
Xregister int bytes;
X{
X  if (write(fd, address, bytes) != bytes) {
X	fprintf(stderr," Error: %s - Write error\n",progname);
X	quit(mypid, SIGINT);
X  }
X}
X
Xlong
Xswap(l)
Xlong l;
X{
X	union {
X		struct {
X			int word1, word2;
X		} words;
X		long n;
X	} u_in, u_out;
X	
X	u_in.n = l;
X	u_out.words.word1 = u_in.words.word2;
X	u_out.words.word2 = u_in.words.word1;
X	return (u_out.n);
X	
X}
X
Xaddmove(pos)
Xlong pos;
X{
X	struct mov_list *newmove;
X
X	newmove = (struct mov_list *)malloc(sizeof(struct mov_list));
X	newmove->pos = pos;
X	newmove->next = moves;
X	moves = newmove;		
X}
X
Xstruct ar_hdr *
Xget_member(fd)
Xint fd;
X{
X	int ret;
X	static struct ar_hdr member;
X	
X	if ((ret = read(fd,  &member, sizeof(struct ar_hdr))) <= 0) 
X		return ((struct ar_hdr *)NULL);
X	if (ret != sizeof(struct ar_hdr)) {
X		fprintf(stderr,"Error: ar corrupted archive ar\n");
X		quit(mypid,SIGINT);
X	}
X
X	/* the archive long format is pdp11 not intel
X	 * therefore we must reformat them for our internal use
X	 */
X
X	member.ar_date = swap(member.ar_date);
X	member.ar_size = swap(member.ar_size);
X	return (&member);
X}
X
Xint
Xopen_archive(filename, opt, to_create)
Xchar *filename;
Xint opt;
X{
X	static unsigned short magic;
X	int fd;
X	
X	/* to_create can have values of 0,1,2 */
X	/* 0 - don't create a file. */
X	/* 1 - create file but use create switch message mode */
X	/* 2 - create file but don't talk about it */
X	
X	if (to_create) {
X		if ((fd = creat(filename, 0644)) < 0) {
X			fprintf(stderr, "Error: %s can not create %s\n",progname, filename);
X			quit(mypid,SIGINT);
X		}
X		if (!create && to_create == 1) fprintf(stderr, "%s:%s created\n", progname, filename);
X		magic = ARMAG;
X		mwrite(fd, &magic, MAGICSIZE);
X		return (fd);
X	}
X	else {
X		if ((fd = open(filename, opt)) < 0) {
X			if (opt == WRITE) 
X				return (open_archive(filename, opt, 1));
X			else {
X				fprintf(stderr, "Error: %s can not open %s\n",progname, filename);
X				quit(mypid,SIGINT);
X			}
X		}
X		/* now check the magic number for ar V7 file */
X		lseek(fd, 0l, 0);
X		read(fd, &magic, MAGICSIZE);
X		if (magic != ARMAG) {
X			fprintf(stderr, "Error: not %s V7 format - %s\n",progname, filename);
X			quit(mypid,SIGINT);
X		}
X		if (major & APPEND)
X			lseek(fd, 0l, 2);	/* seek eof position */
X			
X		return (fd);
X	}		
X}
X
X
Xint
Xrebuild(fd, tempfd)
Xregister int fd, tempfd;
X{
X	register int n;
X
X	/* after we have built the archive to a temporary file and */
X	/* everything has worked out- we copy the archive back to */
X	/* original file */
X		
X	signal(SIGINT, SIG_IGN);
X	close(fd);
X	close(tempfd);
X	fd = open_archive(afile, WRITE, 2);
X	tempfd = open_archive(tmp1, WRITE, 0);
X	while ((n = read(tempfd, buffer, BUFFERSIZE)) > 0) 
X		mwrite(fd, buffer, n);
X	close(tempfd);
X	unlink(tmp1);
X	return (fd);
X}
X
Xprint_mode(mode)
Xshort mode;
X{
X	char g_ex, o_ex, all_ex;
X	char g_rd, o_rd, all_rd;
X	char g_wr, o_wr, all_wr;
X	
X	g_ex = EXEC_GROUP & mode ? 'x' : '-';
X	o_ex = EXEC_OWNER & mode ? 'x' : '-';
X	all_ex = EXEC_ALL & mode ? 'x' : '-';
X	
X	g_ex = SET_GID & mode ? 's' : g_ex;
X	o_ex = SET_UID & mode ? 's' : o_ex;
X	
X	g_rd = READ_GROUP & mode ? 'r' : '-';
X	o_rd = READ_OWNER & mode ? 'r' : '-';
X	all_rd = READ_ALL & mode ? 'r' : '-';
X	
X	g_wr = WRITE_GROUP & mode ? 'w' : '-';
X	o_wr = WRITE_OWNER & mode ? 'w' : '-';
X	all_wr = WRITE_ALL & mode ? 'w' : '-';
X
X	fprintf(stdout,"%c%c%c",o_rd, o_wr, o_ex);
X	fprintf(stdout,"%c%c%c",g_rd, g_wr, g_ex);
X	fprintf(stdout,"%c%c%c",all_rd, all_wr, all_ex);
X}
X
Xprint_header(member)
Xstruct ar_hdr *member;
X{
X	if (verbose) {
X		print_mode(member->ar_mode);
X		fprintf(stdout,"%3.3d",member->ar_uid);
X		fprintf(stdout,"/%-3.3d ",member->ar_gid);
X		fprintf(stdout,"%5.5d",member->ar_size);
X		print_date(member->ar_date);
X	}
X	fprintf(stdout,"%-14.14s\n",member->ar_name);
X}
X
Xprint(fd,member)
Xint fd;
Xstruct ar_hdr *member;
X{
X	int outfd;
X	register int cnt, ret;
X	int do_align;
X
X	if (major & EXTRACT) {
X		if ((outfd = creat(member->ar_name,0666)) < 0) {
X			fprintf(stderr,"Error: %s could not creat %s\n",progname, member->ar_name);
X			quit(mypid,SIGINT);
X		}
X		if (verbose)
X			fprintf(stdout,"x %s\n",member->ar_name);
X	}
X	else
X		outfd = fileno(stdout);
X	
X	for (cnt = member->ar_size; cnt > 0; cnt -= ret) {
X		ret = read(fd, buffer, (cnt < BUFFERSIZE ? cnt : BUFFERSIZE));
X		if (ret > 0)
X			write(outfd,buffer, ret);
X	}
X	if (odd(member->ar_size))
X		lseek(fd,1l,1);		/* realign ourselves */
X		
X	if (major & EXTRACT) {
X		close(outfd);
X		chmod(member->ar_name, member->ar_mode);
X	}		
X}
X
X/* copy a given member from fd1 to fd2 */
Xcopy_member(infd, outfd, member)
Xint infd, outfd;
Xstruct ar_hdr *member;
X{
X	int n, cnt;
X	long m, size;
X
X	/* save copies for our use */
X	m = size = member->ar_size;
X
X	/* format for disk usage */
X	member->ar_size = swap(member->ar_size);
X	member->ar_date = swap(member->ar_date);
X
X	mwrite(outfd, member, sizeof(struct ar_hdr));
X	for (; m > 0; m -= n) {
X		cnt = (m < BUFFERSIZE ? m : BUFFERSIZE);
X		if ((n = read(infd, buffer, cnt)) != cnt) {
X			fprintf(stderr,"Error: %s - read error on %s\n",progname, member->ar_name);
X			quit(mypid, SIGINT);
X		}
X		mwrite(outfd, buffer, n);
X	}	
X	if (odd(size)) {		/* pad to word boundary */
X		mwrite(outfd, buffer, 1);
X		lseek(infd,1l,1);		/* realign reading fd */ 
X	}
X}
X
X/* insert at current offset - name file */
Xinsert(fd, name, mess, oldmember)
Xint fd;
Xchar *name, *mess;
Xstruct ar_hdr *oldmember;
X{
X	static struct ar_hdr member;
X	static struct stat status;
X	int in_fd;
X
X	if (stat(name, &status) < 0) {
X		fprintf(stderr,"Error: %s cannot find file %s\n",progname,name);
X		quit(mypid,SIGINT);
X	}
X	else if ((in_fd = open(name, READ)) < 0) {
X		fprintf(stderr,"Error: %s cannot open file %s\n",progname,name);
X		quit(mypid,SIGINT);
X	}
X	strcpy(member.ar_name, basename(name));
X	member.ar_uid = status.st_uid;
X	member.ar_gid = status.st_gid;
X	member.ar_mode = status.st_mode & 07777;
X	member.ar_date = status.st_mtime;
X	member.ar_size = status.st_size;
X	if (only & ONLY)
X		if (oldmember != (struct ar_hdr *)NULL)  
X			if (member.ar_date <= oldmember->ar_date) {
X				close(in_fd);
X				if (verbose) fprintf(stdout, "not %s - %s\n",mess, name);
X				return (-1);
X			}
X		
X	copy_member(in_fd, fd, &member); 
X	if (verbose) 
X		fprintf(stdout, "%s - %s\n",mess, name);
X	close(in_fd);
X	return (1);
X}
X
Xint
Xar_move(oldfd, arfd,mov)
Xint oldfd, arfd;
Xstruct mov_list *mov;
X{
X	long pos;
X	int cnt, want, a, newfd;
X	struct ar_hdr *member;
X
X	if (local) 
X		tmp2 = mktemp("./ar.2.XXXXXX");
X	else
X		tmp2 = mktemp("/tmp/ar.2.XXXXXX");
X
X	close(oldfd);				/* close old temp file */
X	signal(SIGINT, insert_abort);		/* set new signal handler */
X	newfd = open_archive(tmp2, WRITE, 2);	/* open new tmp file */
X	oldfd = open_archive(tmp1, WRITE, 0);	/* reopen old tmp file */
X
X	/* copy archive till we get to pos_offset */
X	for (pos = pos_offset; pos > 0; pos -= cnt) {
X		want = (pos < BUFFERSIZE ? pos : BUFFERSIZE);
X		if ((cnt = read(oldfd, buffer, want)) > 0)
X			mwrite(newfd, buffer, cnt);
X	}
X	/* if minor = 'a' then skip over posname */
X	if (minor & AFTER) {
X		if ((member = get_member(oldfd)) != NULL)
X			copy_member(oldfd, newfd, member);
X	}
X	/* move members in the library */
X	while (mov != NULL) {
X		lseek(arfd, mov->pos, 0);
X		if ((member = get_member(arfd)) != NULL)
X			copy_member(arfd, newfd, member);
X		mov = mov->next;
X		if (verbose) fprintf(stdout, "m - %s\n", member->ar_name);
X	}
X
X	/* copy rest of library into new tmp file */
X	while ((member = get_member(oldfd)) != NULL) 
X		copy_member(oldfd, newfd, member);
X
X	/* detach old temp file */
X	close(oldfd);
X	unlink(tmp1);
X
X	/* change context temp file */
X	tmp1 = tmp2;
X	return (newfd);
X}
X
Xint
Xar_insert(oldfd, ac, argc, argv)
Xint oldfd;
Xint ac, argc;
Xchar **argv;
X{
X	long pos;
X	int cnt, want, a, newfd;
X	struct ar_hdr *member;
X
X	if (local) 
X		tmp2 = mktemp("./ar.2.XXXXXX");
X	else
X		tmp2 = mktemp("/tmp/ar.2.XXXXXX");
X
X	close(oldfd);				/* close old temp file */
X	signal(SIGINT, insert_abort);		/* set new signal handler */
X	newfd = open_archive(tmp2, WRITE, 2);	/* open new tmp file */
X	oldfd = open_archive(tmp1, WRITE, 0);	/* reopen old tmp file */
X
X	/* copy archive till we get to pos_offset */
X	for (pos = pos_offset; pos > 0; pos -= cnt) {
X		want = (pos < BUFFERSIZE ? pos : BUFFERSIZE);
X		if ((cnt = read(oldfd, buffer, want)) > 0)
X			mwrite(newfd, buffer, cnt);
X	}
X	/* if minor = 'a' then skip over posname */
X	if (minor & AFTER) {
X		if ((member = get_member(oldfd)) != NULL)
X			copy_member(oldfd, newfd, member);
X	}
X	
X	/* copy new members into the library */
X	for (a = ac+1; a <= argc; ++a)
X		if (argv[a-1] && *argv[a-1] != '\0') {
X			insert(newfd, argv[a-1], "a", (struct ar_hdr *)NULL);
X			*argv[a-1] = '\0';
X		}
X
X	/* copy rest of library into new tmp file */
X	while ((member = get_member(oldfd)) != NULL) 
X		copy_member(oldfd, newfd, member);
X
X	/* detach old temp file */
X	close(oldfd);
X	unlink(tmp1);
X
X	/* change context temp file */
X	tmp1 = tmp2;
X	return (newfd);
X}
X
Xar_common(ac, argc, argv)
Xint ac, argc;
Xchar **argv;
X{
X	int a, fd;
X	struct ar_hdr *member;
X
X	fd = open_archive(afile, READ, 0);
X	while ((member = get_member(fd)) != NULL) {
X		if (ac < argc) {
X			for (a = ac+1; a <= argc; ++a) {
X				if (strcmp(basename(argv[a-1]),member->ar_name) == 0) {
X					if (major & TABLE)
X						print_header(member);
X					else if (major & (PRINT | EXTRACT))
X						print(fd, member);
X					*argv[a-1] = '\0';
X					break;
X				}
X				else if (major & (PRINT | EXTRACT))
X					lseek(fd, (long)even(member->ar_size), 1);
X			}
X		}
X		else {
X			if (major & TABLE)
X				print_header(member);
X			else if (major & (PRINT | EXTRACT))
X				print(fd, member);
X		}
X		if (major & TABLE)
X			lseek(fd, (long)even(member->ar_size), 1);
X	}
X}
X
Xar_members(ac, argc, argv)
Xint ac, argc;
Xchar **argv;
X{
X	int a, fd, tempfd, rc;
X	struct ar_hdr *member;
X	long *lpos;
X
X	fd = open_archive(afile, WRITE, 0);
X	tempfd = open_archive(tmp1, WRITE, 2);
X	while ((member = get_member(fd)) != NULL) {
X
X		/* if posname specified check for our member */
X		/* if our member save his starting pos in our working file*/
X		if (posname && strcmp(posname, member->ar_name) == 0)
X			pos_offset = tell(tempfd) - MAGICSIZE;
X
X		if (ac < argc) { /* we have a list of members to check */
X			for (a = ac+1; a <= argc; ++a)
X				if (strcmp(basename(argv[a-1]),member->ar_name) == 0) {
X					if (major & REPLACE) {
X						if (insert(tempfd,argv[a-1],"r", member) < 0)
X							copy_member(fd, tempfd, member);
X						else 
X							lseek(fd, (long)even(member->ar_size), 1);
X					}
X					else if (major & MOVE) {
X						/* cheat by saving pos in archive */
X						addmove((tell(fd) - sizeof(struct ar_hdr)));
X						lseek(fd, (long)even(member->ar_size), 1);
X					}
X					*argv[a-1] = '\0';
X					break;
X				}
X		}
X		if (ac >= argc || a > argc) 	/*nomatch on a member name */
X			copy_member(fd, tempfd, member);
X		else if (major & DELETE) {
X			if (verbose) fprintf(stdout,"d - %s\n",member->ar_name);
X			lseek(fd, (long)even(member->ar_size), 1);
X		}
X	}
X	if (major & MOVE) {
X		if (posname == NULL) 
X			pos_offset = lseek(fd, 0l, 2);
X		else if (pos_offset == (-1)) {
X			fprintf(stderr,"Error: %s cannot find file %s\n",progname,posname);
X			quit(mypid,SIGINT);
X		}
X		tempfd = ar_move(tempfd, fd, moves);
X	}
X	else if (major & REPLACE) {
X		/* take care to add left overs */
X		/* if the posname is not found we just add to end of ar */
X		if (posname && pos_offset != (-1)) {
X			tempfd = ar_insert(tempfd, ac, argc, argv);
X		}
X		else {
X			for (a = ac+1; a <= argc; ++a)
X				if (*argv[a-1]) {
X					insert(tempfd, argv[a-1], "a", (struct ar_hdr *)NULL);
X					*argv[a-1] = '\0';
X				}
X		}
X	}
X	fd = rebuild(fd, tempfd);
X	close(fd);
X}
X
Xappend_members(ac, argc, argv)
Xint ac, argc;
Xchar **argv;
X{
X	int a, fd;
X	struct ar_hdr *member;
X	
X	/* quickly append members don't worry about dups in ar */
X	fd = open_archive(afile, WRITE, 0);
X	if (ac < argc) {
X		if (odd(lseek(fd, 0l, 2)))
X			mwrite(fd, buffer, 1);
X		/* while not end of member list insert member at end */
X		for (a = ac+1; a <= argc; ++a) {
X			insert(fd, argv[a-1], "a", (struct ar_hdr *)NULL);
X			*argv[a-1] = '\0';
X		}
X	}
X	close(fd);
X}
/
echo x - basename.c
gres '^X' '' > basename.c << '/'
X#include <stdio.h>
X
Xchar *basename(path)
Xchar *path;
X{
X  register char *ptr = path;
X  register char *last = (char *)NULL;
X
X  while (*ptr != '\0') {
X	if (*ptr == '/')
X		last = ptr;
X	ptr++;
X  }
X  if (last == (char *)NULL)
X	return path;
X  if (*(last + 1) == '\0') {
X	*last = '\0';
X	return basename(path);
X  }
X  return last + 1;
X}
/
echo x - date.c
gres '^X' '' > date.c << '/'
X#include <stdio.h>
X
X#define MINUTE	60L
X#define HOUR	(60L * MINUTE)
X#define DAY	(24L * HOUR)
X#define YEAR	(365L * DAY)
X#define LYEAR	(366L * DAY)
X
Xint mo[] = {
X  31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
X};
X
Xchar *moname[] = {
X  " Jan ", " Feb ", " Mar ", " Apr ", " May ", " Jun ",
X  " Jul ", " Aug ", " Sep ", " Oct ", " Nov ", " Dec "
X};
X
X/* Print the date.  This only works from 1970 to 2099. */
Xprint_date(t)
Xlong t;
X{
X  int i, year, day, month, hour, minute;
X  long length, time(), original;
X
X  year = 1970;
X  original = t;
X  while (t > 0) {
X	length = (year % 4 == 0 ? LYEAR : YEAR);
X	if (t < length)
X		break;
X	t -= length;
X	year++;
X  }
X
X /* Year has now been determined.  Now the rest. */
X  day = (int) (t / DAY);
X  t -= (long) day * DAY;
X  hour = (int) (t / HOUR);
X  t -= (long) hour * HOUR;
X  minute = (int) (t / MINUTE);
X
X /* Determine the month and day of the month. */
X  mo[1] = (year % 4 == 0 ? 29 : 28);
X  month = 0;
X  i = 0;
X  while (day >= mo[i]) {
X	month++;
X	day -= mo[i];
X	i++;
X  }
X
X  /* At this point, 'year', 'month', 'day', 'hour', 'minute'  ok */
X  fprintf(stdout, "%s%2.2d ",moname[month],++day);
X  if (time((long *)NULL) - original >= YEAR / 2L)
X	fprintf(stdout,"%4.4D ",(long)year);
X  else 
X	fprintf(stdout,"%02.2d:%02.2d ",hour, minute);
X}
X
/
-------------------------------end-here--------------------------------------
Monty Walls, Tech. Support. MIS
Oklahoma Tax Commission
2501 N. Lincoln
OKC, OK, 73194
4
4