#define	NARMAG	0177545
#define FLGA flg[0]
#define FLGB flg[1]
#define FLGI flg[2]
#define FLGN flg[3]
#define FLGU flg[4]
#define FLGV flg[5]
#define FLGS flg[6]
struct
{
	char	name[14];
#ifdef	LONG
	long	date;
#endif
#ifndef	LONG
	char	*date[2];
#endif
	char	uid;
	char	gid;
	int	mode;
#ifdef	LONG
	long	size;
#endif
#ifndef	LONG
	int	size0;
	char	*size1;
#endif
} arbuf;
struct
{
	char	minor;
	char	major;
	int	inumber;
	int	flags;
	char	nlinks;
	char	usrid;
	char	grpid;
	char	siz0;
	char	*siz1;
	int	addr[8];
#ifdef	LONG
	long	adate;
	long	mdate;
#endif
#ifndef	LONG
	char	*adate[2];
	char	*mdate[2];
#endif
} stbuf;

#define	SKIP	1
#define	IODD	2
#define	OODD	4
#define	HEAD	8

char	*man	{ "mrxltdp" };
char	*opt	{ "uvnbais" };

#ifdef	LONG
long	itol();
#endif
int	done();
int	rcmd();
int	dcmd();
int	xcmd();
int	tcmd();
int	pcmd();
int	mcmd();
int	(*comfun)();
char	flg[7];
char	**namv;
int	namc;
char	*arnam;
char	*ponam;
char	*tfnam;
char	*tf1nam;
char	*tf2nam;
char	*file;
char	name[16];
int	af;
int	tf;
int	tf1;
int	tf2;
int	bastate;
int	buf[256];
int	errcode;

main(argc, argv)
char *argv[];
{
	register i;
	register char *cp;

	for(i=1; i<4; i++)
		if((signal(i, 1) & 1) == 0)
			signal(i, done);
	if(argc < 3)
		usage();
	for(cp = argv[1]; *cp; cp++)
	switch(*cp) {
	case 'v':	/* verbose mode */
		FLGV++;
		continue;
	case 'u':	/* update (replace iff file newer than archive file) */
		FLGU++;
		continue;
	case 'n':	/* interactive rename */
		FLGN++;
		continue;
	case 'a':	/* append files after select file*/
		FLGA++;
		continue;
	case 'b':	/* before - see insert */
		FLGB++;
		continue;
	case 'i':	/* insert files before select file */
		FLGI++;
		continue;

	case 's':	/* suppress error messages */
		FLGS++;
		continue;

	case 'r':	/* replace (add) named files */
		setcom(rcmd);
		continue;

	case 'd':	/* delete named files */
		setcom(dcmd);
		continue;

	case 'x':	/* extract named files */
		setcom(xcmd);
		continue;

	case 'l':	/* 'ls -l' for archive file */
		FLGV++;
	case 't':	/* 'ls' for archive file */
		setcom(tcmd);
		continue;

	case 'p':	/* print named files */
		setcom(pcmd);
		continue;

	case 'm':	/* move (reshuffle)  named files within archive */
		setcom(mcmd);
		continue;

	default:
		error("bad option `%c'\n", *cp);
		done();
	}
	if(FLGI)
		FLGB++;
	if(FLGA || FLGB) {
		bastate = 1;
		ponam = trim(argv[2]);
		argv++;
		argc--;
		if(argc < 3)
			usage();
	}
	arnam = argv[2];
	namv = argv+3;
	namc = argc-3;
	if(comfun == 0) {
		if(FLGU == 0 && bastate == 0) {
			error("one of [%s] must be specified\n", man);
			done();
		}
		if(FLGU || FLGI)
			setcom(rcmd);
		else
			setcom(mcmd);
	}
	(*comfun)();
	notfound();
	done();
}

setcom(fun)
int (*fun)();
{

	if(comfun != 0) {
		error("only one of [%s] allowed\n", man);
		done();
	}
	comfun = fun;
}

rcmd()
{
	register f;

	init();
	if(getaf()) {
		printf("creating %s\n", arnam);
		cleanup();
		return;
	}
	while(!getdir()) {
		bamatch();
		if(namc == 0 || match()) {
			f = stats();
			if(f < 0) {
				if(namc)
					error("cannot open %s\n", file);
				goto cp;
			}
			if(FLGU)
#ifdef	LONG
				if(stbuf.mdate <= arbuf.date) {
#endif
#ifndef	LONG
				if(stbuf.mdate[0] < arbuf.date[0] ||
				  (stbuf.mdate[0] == arbuf.date[0] &&
				   stbuf.mdate[1] <= arbuf.date[1])) {
#endif
					close(f);
					goto cp;
				}
			if(FLGN) file = rename();
			mesg('r');
			copyfil(af, -1, IODD+SKIP);
			movefil(f);
			continue;
		}
	cp:
		mesg('c');
		copyfil(af, tf, IODD+OODD+HEAD);
	}
	cleanup();
}

dcmd()
{
	register char *s;

	init();
	if(getaf())
		noar();
	while(!getdir()) {
		if(match() || (FLGN && namc == 0)) {
			if(FLGN) {
				printf("? - %s?", file);
				flush();
				if(!read(0, s=buf, sizeof buf) || *s!='y')
					goto cp;
			}
			mesg('d');
			copyfil(af, -1, IODD+SKIP);
			continue;
		}
	cp:
		mesg('c');
		copyfil(af, tf, IODD+OODD+HEAD);
	}
	install();
}

xcmd()
{
	register f;

	if(getaf())
		noar();
	while(!getdir()) {
		if(namc == 0 || match()) {
			if(FLGU && stat(file,&stbuf) != -1
#ifdef	LONG
				&& stbuf.mdate >= arbuf.date)
#endif
#ifndef	LONG
				&& stbuf.mdate[0] > arbuf.date[0] ||
				(stbuf.mdate[0] == arbuf.date[0] &&
				stbuf.mdate[1] >= arbuf.date[1]))
#endif
					goto sk;
			f = creat(file, arbuf.mode & 07777);
			if(f < 0) {
				error("%s cannot create\n", file);
				goto sk;
			}
			mesg('x');
			copyfil(af, f, IODD);
			close(f);
			continue;
		}
	sk:
		mesg('c');
		copyfil(af, -1, IODD+SKIP);
	}
}

pcmd()
{

	if(getaf())
		noar();
	while(!getdir()) {
		if(namc == 0 || match()) {
			if(FLGV) printf("\n\n%.14s:\n\n",file);
			copyfil(af, 1, IODD);
			continue;
		}
		copyfil(af, -1, IODD+SKIP);
	}
}

mcmd()
{
	register char *fro, *to;

	init();
	if(getaf())
		noar();
	tf2nam = mktemp("/tmp/v2XXXXX");
	close(creat(tf2nam, 0600));
	tf2 = open(tf2nam, 2);
	if(tf2 < 0) {
		error("cannot create third temp\n");
		done();
	}
	while(!getdir()) {
		bamatch();
		if(match()) {
			if(FLGN) {
				fro = trim(rename());
				if(fro != file)
				for(file = to = arbuf.name;
					to < arbuf.name+14;
					*to++ = *fro ? *fro++ : '\0');
			}
			mesg('m');
			copyfil(af, tf2, IODD+OODD+HEAD);
			continue;
		}
		mesg('c');
		copyfil(af, tf, IODD+OODD+HEAD);
	}
	install();
}

tcmd()
{

	if(getaf())
		noar();
	while(!getdir()) {
		if(namc == 0 || match()) {
			if(FLGV)
				longt();
			printf("%s\n", trim(file));
		}
		copyfil(af, -1, IODD+SKIP);
	}
}

init()
{

	tfnam = mktemp("/tmp/vXXXXX");
	close(creat(tfnam, 0600));
	tf = open(tfnam, 2);
	if(tf < 0) {
		error("cannot create temp file\n");
		done();
	}
	buf[0] = NARMAG;
	write(tf, buf, 2);
}

getaf()
{

	af = open(arnam, 0);
	if(af < 0)
		return(1);
	buf[0] = 0;
	read(af, buf, 2);
	if(buf[0] != NARMAG) {
		error("%s not in archive format\n", arnam);
		done();
	}
	return(0);
}

usage()
{
	error("usage: ar [%s][%s] archive files ...\n", opt, man);
	done();
}

noar()
{

	error("%s does not exist\n", arnam);
	done();
}

done()
{

	if(tfnam)
		unlink(tfnam);
	if(tf1nam)
		unlink(tf1nam);
	if(tf2nam)
		unlink(tf2nam);
	exit(errcode);
}

notfound()
{
	register i;

	for(i=0; i<namc; i++)
		if(namv[i])
			error("%s not found\n", namv[i]);
}

cleanup()
{
	register i, f;

	for(i=0; i<namc; i++) {
		file = namv[i];
		if(file == 0)
			continue;
		namv[i] = 0;
		f = stats();
		if(f < 0) {
			error("%s cannot open\n", file);
			continue;
		}
		if(FLGN) {
			file = rename();
		}
		mesg('a');
		movefil(f);
	}
	install();
}

install()
{
	register i;

	for(i=1; i<4; i++)
		signal(i, 1);
	close(af);
	af = creat(arnam, 0666);
	if(af < 0) {
		error("cannot create %s\n", arnam);
		done();
	}
	seek(tf, 0, 0);
	while((i = read(tf, buf, 512)) > 0)
		write(af, buf, i);
	if(tf2nam) {
		seek(tf2, 0, 0);
		while((i = read(tf2, buf, 512)) > 0)
			write(af, buf, i);
	}
	if(tf1nam) {
		seek(tf1, 0, 0);
		while((i = read(tf1, buf, 512)) > 0)
			write(af, buf, i);
	}
}

/*
 * insert the file 'file'
 * into the temporary file
 */
movefil(f)
{
	register char *cp;
	register i;

	cp = trim(file);
	for(i=0; i<14; i++)
		if(arbuf.name[i] = *cp)
			cp++;
#ifdef	LONG
	arbuf.size = itol(stbuf.siz0&0377, stbuf.siz1);
	arbuf.date = stbuf.mdate;
#endif
#ifndef	LONG
	arbuf.size0 = stbuf.siz0&0377;
	arbuf.size1 = stbuf.siz1;
	arbuf.date[0] = stbuf.mdate[0];
	arbuf.date[1] = stbuf.mdate[1];
#endif
	arbuf.uid = stbuf.usrid;
	arbuf.gid = stbuf.grpid;
	arbuf.mode = stbuf.flags;
	copyfil(f, tf, OODD+HEAD);
	close(f);
}

stats()
{
	register f;

	f = open(file, 0);
	if(f < 0)
		return(f);
	if(fstat(f, &stbuf) < 0) {
		close(f);
		return(-1);
	}
	return(f);
}

/*
 * copy next file
 * size given in arbuf
 */
copyfil(fi, fo, flag)
{
	register char *i, *o;

	if(flag & HEAD)
		write(fo, &arbuf, sizeof arbuf);
#ifdef	LONG
	while(arbuf.size > 0) {
		i = o = 512;
		if(arbuf.size < i) {
			i = o = arbuf.size;
#endif
#ifndef LONG
	while(arbuf.size0 >= 0 && arbuf.size1) {
		i = o = 512;
		if(arbuf.size0==0 && arbuf.size1<i) {
			i = o = arbuf.size1;
#endif
			if(i&1) {
				if(flag & IODD)
					i++;
				if(flag & OODD)
					o++;
			}
		}
		if(read(fi, buf, i) != i)
			phserr();
		if((flag & SKIP) == 0)
			write(fo, buf, o);
#ifdef	LONG
		arbuf.size =- 512;
#endif
#ifndef	LONG
		lsubi(&arbuf.size0, 512);
#endif
	}
}

getdir()
{
	register i;

	i = read(af, &arbuf, sizeof arbuf);
	if(i != sizeof arbuf) {
		if(tf1nam) {
			i = tf;
			tf = tf1;
			tf1 = i;
		}
		return(1);
	}
	for(i=0; i<14; i++)
		name[i] = arbuf.name[i];
	file = name;
	return(0);
}

match()
{
	register i;

	for(i=0; i<namc; i++) {
		if(namv[i] == 0)
			continue;
		if(equal(trim(namv[i]), file)) {
			file = namv[i];
			namv[i] = 0;
			return(1);
		}
	}
	return(0);
}

bamatch()
{
	register f;

	switch(bastate) {

	case 1:
		if(!equal(file, ponam))
			return;
		bastate = 2;
		if(FLGA)
			return;

	case 2:
		bastate = 0;
		tf1nam = mktemp("/tmp/v1XXXXX");
		close(creat(tf1nam, 0600));
		f = open(tf1nam, 2);
		if(f < 0) {
			error("cannot create second temp\n");
			return;
		}
		tf1 = tf;
		tf = f;
	}
}

equal(s1, s2)
char *s1, *s2;
{
	register char *p1, *p2;

	p1 = s1;
	p2 = s2;
	while(*p1++ == *p2)
		if(*p2++ == 0)
			return(1);
	return(0);
}

phserr()
{

	error("phase error on %s\n", file);
	exit(16);
}

mesg(c)
{

	if(FLGV)
		if(c != 'c' || FLGV > 1)
			printf("%c - %s\n", c, file);
}

rename()
	{
	register char *p, *s;
	char name_buf[512];

	printf("n - %s?",file);
	flush();

	if(!read(0, p=s=name_buf, sizeof name_buf))
		*s=0;
	while(*p)
		switch(*p++) {
		case '\n':
			*--p = 0;
			continue;

		case ':':
		case '*':
		case '[':
		case ']':
		case '(':
		case ')':
		case '=':
		case '?':
		case '<':
		case '>':
		case '"':
			error("illegal char '%c'\n", p[-1]);
			*s=0;
		}
	return(*s ? s : file);
}

trim(s)
char *s;
{
	register char *p1, *p2;

	for(p1 = s; *p1; p1++)
		;
	while(p1 > s) {
		if(*--p1 != '/')
			break;
		*p1 = 0;
	}
	p2 = s;
	for(p1 = s; *p1; )
		if(*p1++ == '/')
			p2 = p1;
	return(p2);
}

#define	IFMT	060000
#define	ISARG	01000
#define	LARGE	010000
#define	SUID	04000
#define	SGID	02000
#define	ROWN	0400
#define	WOWN	0200
#define	XOWN	0100
#define	RGRP	040
#define	WGRP	020
#define	XGRP	010
#define	ROTH	04
#define	WOTH	02
#define	XOTH	01
#define	STXT	01000

longt()
{
	register char *cp;
	register t;

	pmode();
	printf("%3d/%1d", arbuf.uid&0377, arbuf.gid&0377);
#ifdef	LONG
	printf("%8s", locv(arbuf.size));
	cp = ctime(&arbuf.date);
#endif
#ifndef	LONG
	printf("%8s", locv(arbuf.size0,arbuf.size1));
	cp = ctime(arbuf.date);
#endif
	printf(" %-6.6s %-4.4s ", cp+4, cp+20);
}

int	m1[] { 1, ROWN, 'r', '-' };
int	m2[] { 1, WOWN, 'w', '-' };
int	m3[] { 2, SUID, 's', XOWN, 'x', '-' };
int	m4[] { 1, RGRP, 'r', '-' };
int	m5[] { 1, WGRP, 'w', '-' };
int	m6[] { 2, SGID, 's', XGRP, 'x', '-' };
int	m7[] { 1, ROTH, 'r', '-' };
int	m8[] { 1, WOTH, 'w', '-' };
int	m9[] { 2, STXT, 't', XOTH, 'x', '-' };

int	*m[] { m1, m2, m3, m4, m5, m6, m7, m8, m9};

pmode()
{
	register int **mp;

	for (mp = &m[0]; mp < &m[9];)
		select(*mp++);
}

select(pairp)
int *pairp;
{
	register int n, *ap;

	ap = pairp;
	n = *ap++;
	while (--n>=0 && (arbuf.mode&*ap++)==0)
		ap++;
	putchar(*ap);
}

#ifndef	LONG
lsubi(lng,short)
	int *lng;
	{
	register int *l;

	l=lng;

	*l++ =- (l[1]>=0 && l[1] < short);
	*l =- short;
}
#endif

error(fmt, arg1, arg2)
	{
	extern fout;

	if(FLGS == 0) {
		fout = 2;
		printf(fmt, arg1, arg2);
		fout = 1;
	}
	errcode++;
}
