xref: /plan9/sys/src/cmd/tar.c (revision 499069debb03e99ea217d35fd87fb49e96918a92)
10fb3f58eSDavid du Colombier /*
20fb3f58eSDavid du Colombier  * tar - `tape archiver', actually usable on any medium.
30fb3f58eSDavid du Colombier  *	POSIX "ustar" compliant when extracting, and by default when creating.
40fb3f58eSDavid du Colombier  *	this tar attempts to read and write multiple Tblock-byte blocks
50fb3f58eSDavid du Colombier  *	at once to and from the filesystem, and does not copy blocks
60fb3f58eSDavid du Colombier  *	around internally.
70fb3f58eSDavid du Colombier  */
80fb3f58eSDavid du Colombier 
93e12c5d1SDavid du Colombier #include <u.h>
103e12c5d1SDavid du Colombier #include <libc.h>
110fb3f58eSDavid du Colombier #include <fcall.h>		/* for %M */
120fb3f58eSDavid du Colombier #include <String.h>
133e12c5d1SDavid du Colombier 
140fb3f58eSDavid du Colombier /*
150fb3f58eSDavid du Colombier  * modified versions of those in libc.h; scans only the first arg for
160fb3f58eSDavid du Colombier  * keyletters and options.
170fb3f58eSDavid du Colombier  */
180fb3f58eSDavid du Colombier #define	TARGBEGIN {\
190fb3f58eSDavid du Colombier 	(argv0 || (argv0 = *argv)), argv++, argc--;\
200fb3f58eSDavid du Colombier 	if (argv[0]) {\
210fb3f58eSDavid du Colombier 		char *_args, *_argt;\
220fb3f58eSDavid du Colombier 		Rune _argc;\
230fb3f58eSDavid du Colombier 		_args = &argv[0][0];\
240fb3f58eSDavid du Colombier 		_argc = 0;\
250fb3f58eSDavid du Colombier 		while(*_args && (_args += chartorune(&_argc, _args)))\
260fb3f58eSDavid du Colombier 			switch(_argc)
270fb3f58eSDavid du Colombier #define	TARGEND	SET(_argt); USED(_argt);USED(_argc);USED(_args); \
280fb3f58eSDavid du Colombier 	argc--, argv++; } \
290fb3f58eSDavid du Colombier 	USED(argv); USED(argc); }
300fb3f58eSDavid du Colombier #define	TARGC() (_argc)
3140ef9009SDavid du Colombier 
320fb3f58eSDavid du Colombier #define ROUNDUP(a, b)	(((a) + (b) - 1)/(b))
330fb3f58eSDavid du Colombier #define BYTES2TBLKS(bytes) ROUNDUP(bytes, Tblock)
340fb3f58eSDavid du Colombier 
35bbd061d4SDavid du Colombier /* read big-endian binary integers; args must be (uchar *) */
36bbd061d4SDavid du Colombier #define	G2BEBYTE(x)	(((x)[0]<<8)  |  (x)[1])
37bbd061d4SDavid du Colombier #define	G3BEBYTE(x)	(((x)[0]<<16) | ((x)[1]<<8)  |  (x)[2])
38bbd061d4SDavid du Colombier #define	G4BEBYTE(x)	(((x)[0]<<24) | ((x)[1]<<16) | ((x)[2]<<8) | (x)[3])
39bbd061d4SDavid du Colombier #define	G8BEBYTE(x)	(((vlong)G4BEBYTE(x)<<32) | (u32int)G4BEBYTE((x)+4))
40bbd061d4SDavid du Colombier 
410fb3f58eSDavid du Colombier typedef vlong Off;
424f281771SDavid du Colombier typedef char *(*Refill)(int ar, char *bufs, int justhdr);
430fb3f58eSDavid du Colombier 
440fb3f58eSDavid du Colombier enum { Stdin, Stdout, Stderr };
450fb3f58eSDavid du Colombier enum { Rd, Wr };			/* pipe fd-array indices */
460fb3f58eSDavid du Colombier enum { Output, Input };
470fb3f58eSDavid du Colombier enum { None, Toc, Xtract, Replace };
484f281771SDavid du Colombier enum { Alldata, Justnxthdr };
4940ef9009SDavid du Colombier enum {
500fb3f58eSDavid du Colombier 	Tblock = 512,
510fb3f58eSDavid du Colombier 	Namsiz = 100,
5240ef9009SDavid du Colombier 	Maxpfx = 155,		/* from POSIX */
530fb3f58eSDavid du Colombier 	Maxname = Namsiz + 1 + Maxpfx,
54bbd061d4SDavid du Colombier 	Binsize = 0x80,		/* flag in size[0], from gnu: positive binary size */
55bbd061d4SDavid du Colombier 	Binnegsz = 0xff,	/* flag in size[0]: negative binary size */
56bbd061d4SDavid du Colombier 
57bbd061d4SDavid du Colombier 	Nblock = 40,		/* maximum blocksize */
58bbd061d4SDavid du Colombier 	Dblock = 20,		/* default blocksize */
590fb3f58eSDavid du Colombier 	DEBUG = 0,
6040ef9009SDavid du Colombier };
6140ef9009SDavid du Colombier 
6240ef9009SDavid du Colombier /* POSIX link flags */
6340ef9009SDavid du Colombier enum {
6440ef9009SDavid du Colombier 	LF_PLAIN1 =	'\0',
6540ef9009SDavid du Colombier 	LF_PLAIN2 =	'0',
6640ef9009SDavid du Colombier 	LF_LINK =	'1',
6740ef9009SDavid du Colombier 	LF_SYMLINK1 =	'2',
685437ee90SDavid du Colombier 	LF_SYMLINK2 =	's',		/* 4BSD used this */
6940ef9009SDavid du Colombier 	LF_CHR =	'3',
7040ef9009SDavid du Colombier 	LF_BLK =	'4',
7140ef9009SDavid du Colombier 	LF_DIR =	'5',
7240ef9009SDavid du Colombier 	LF_FIFO =	'6',
7340ef9009SDavid du Colombier 	LF_CONTIG =	'7',
7440ef9009SDavid du Colombier 	/* 'A' - 'Z' are reserved for custom implementations */
7540ef9009SDavid du Colombier };
7640ef9009SDavid du Colombier 
7740ef9009SDavid du Colombier #define islink(lf)	(isreallink(lf) || issymlink(lf))
7840ef9009SDavid du Colombier #define isreallink(lf)	((lf) == LF_LINK)
7940ef9009SDavid du Colombier #define issymlink(lf)	((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2)
800fb3f58eSDavid du Colombier 
810fb3f58eSDavid du Colombier typedef union {
820fb3f58eSDavid du Colombier 	uchar	data[Tblock];
830fb3f58eSDavid du Colombier 	struct {
840fb3f58eSDavid du Colombier 		char	name[Namsiz];
853e12c5d1SDavid du Colombier 		char	mode[8];
863e12c5d1SDavid du Colombier 		char	uid[8];
873e12c5d1SDavid du Colombier 		char	gid[8];
883e12c5d1SDavid du Colombier 		char	size[12];
893e12c5d1SDavid du Colombier 		char	mtime[12];
903e12c5d1SDavid du Colombier 		char	chksum[8];
913e12c5d1SDavid du Colombier 		char	linkflag;
920fb3f58eSDavid du Colombier 		char	linkname[Namsiz];
930fb3f58eSDavid du Colombier 
9440ef9009SDavid du Colombier 		/* rest are defined by POSIX's ustar format; see p1003.2b */
9540ef9009SDavid du Colombier 		char	magic[6];	/* "ustar" */
9640ef9009SDavid du Colombier 		char	version[2];
9740ef9009SDavid du Colombier 		char	uname[32];
9840ef9009SDavid du Colombier 		char	gname[32];
9940ef9009SDavid du Colombier 		char	devmajor[8];
10040ef9009SDavid du Colombier 		char	devminor[8];
1010fb3f58eSDavid du Colombier 		char	prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
1020fb3f58eSDavid du Colombier 	};
1030fb3f58eSDavid du Colombier } Hdr;
1043e12c5d1SDavid du Colombier 
1050fb3f58eSDavid du Colombier typedef struct {
1060fb3f58eSDavid du Colombier 	char	*comp;
1070fb3f58eSDavid du Colombier 	char	*decomp;
1080fb3f58eSDavid du Colombier 	char	*sfx[4];
1090fb3f58eSDavid du Colombier } Compress;
1103e12c5d1SDavid du Colombier 
1110fb3f58eSDavid du Colombier static Compress comps[] = {
1120fb3f58eSDavid du Colombier 	"gzip",		"gunzip",	{ ".tar.gz", ".tgz" },	/* default */
1130fb3f58eSDavid du Colombier 	"compress",	"uncompress",	{ ".tar.Z",  ".tz" },
1140fb3f58eSDavid du Colombier 	"bzip2",	"bunzip2",	{ ".tar.bz", ".tbz",
1150fb3f58eSDavid du Colombier 					  ".tar.bz2",".tbz2" },
1160fb3f58eSDavid du Colombier };
1173e12c5d1SDavid du Colombier 
1180fb3f58eSDavid du Colombier typedef struct {
1190fb3f58eSDavid du Colombier 	int	kid;
1200fb3f58eSDavid du Colombier 	int	fd;	/* original fd */
1210fb3f58eSDavid du Colombier 	int	rfd;	/* replacement fd */
1220fb3f58eSDavid du Colombier 	int	input;
1230fb3f58eSDavid du Colombier 	int	open;
1240fb3f58eSDavid du Colombier } Pushstate;
12540ef9009SDavid du Colombier 
1260fb3f58eSDavid du Colombier #define OTHER(rdwr) (rdwr == Rd? Wr: Rd)
1270fb3f58eSDavid du Colombier 
1280fb3f58eSDavid du Colombier static int debug;
1290fb3f58eSDavid du Colombier static int verb;
1300fb3f58eSDavid du Colombier static int posix = 1;
1310fb3f58eSDavid du Colombier static int docreate;
1320fb3f58eSDavid du Colombier static int aruid;
1330fb3f58eSDavid du Colombier static int argid;
13423605fa3SDavid du Colombier static int relative = 1;
1350fb3f58eSDavid du Colombier static int settime;
1360fb3f58eSDavid du Colombier static int verbose;
1370fb3f58eSDavid du Colombier static int docompress;
1380fb3f58eSDavid du Colombier static int keepexisting;
1393366099cSDavid du Colombier static Off blkoff;	/* offset of the current archive block (not Tblock) */
1404f281771SDavid du Colombier static Off nexthdr;
1410fb3f58eSDavid du Colombier 
1420fb3f58eSDavid du Colombier static int nblock = Dblock;
1430fb3f58eSDavid du Colombier static char *usefile;
1440fb3f58eSDavid du Colombier static char origdir[Maxname*2];
1450fb3f58eSDavid du Colombier static Hdr *tpblk, *endblk;
1460fb3f58eSDavid du Colombier static Hdr *curblk;
1470fb3f58eSDavid du Colombier 
1480fb3f58eSDavid du Colombier static void
1490fb3f58eSDavid du Colombier usage(void)
15040ef9009SDavid du Colombier {
1510fb3f58eSDavid du Colombier 	fprint(2, "usage: %s {crtx}[PRTfgkmpuvz] [archive] file1 file2...\n",
1520fb3f58eSDavid du Colombier 		argv0);
1530fb3f58eSDavid du Colombier 	exits("usage");
1540fb3f58eSDavid du Colombier }
1550fb3f58eSDavid du Colombier 
1560fb3f58eSDavid du Colombier /* compression */
1570fb3f58eSDavid du Colombier 
1580fb3f58eSDavid du Colombier static Compress *
1590fb3f58eSDavid du Colombier compmethod(char *name)
1600fb3f58eSDavid du Colombier {
1610fb3f58eSDavid du Colombier 	int i, nmlen = strlen(name), sfxlen;
1620fb3f58eSDavid du Colombier 	Compress *cp;
1630fb3f58eSDavid du Colombier 
1640fb3f58eSDavid du Colombier 	for (cp = comps; cp < comps + nelem(comps); cp++)
1650fb3f58eSDavid du Colombier 		for (i = 0; i < nelem(cp->sfx) && cp->sfx[i]; i++) {
1660fb3f58eSDavid du Colombier 			sfxlen = strlen(cp->sfx[i]);
1670fb3f58eSDavid du Colombier 			if (nmlen > sfxlen &&
1680fb3f58eSDavid du Colombier 			    strcmp(cp->sfx[i], name + nmlen - sfxlen) == 0)
1690fb3f58eSDavid du Colombier 				return cp;
1700fb3f58eSDavid du Colombier 		}
1710fb3f58eSDavid du Colombier 	return docompress? comps: nil;
1720fb3f58eSDavid du Colombier }
1730fb3f58eSDavid du Colombier 
1740fb3f58eSDavid du Colombier /*
1750fb3f58eSDavid du Colombier  * push a filter, cmd, onto fd.  if input, it's an input descriptor.
1760fb3f58eSDavid du Colombier  * returns a descriptor to replace fd, or -1 on error.
1770fb3f58eSDavid du Colombier  */
1780fb3f58eSDavid du Colombier static int
1790fb3f58eSDavid du Colombier push(int fd, char *cmd, int input, Pushstate *ps)
1800fb3f58eSDavid du Colombier {
1810fb3f58eSDavid du Colombier 	int nfd, pifds[2];
1820fb3f58eSDavid du Colombier 	String *s;
1830fb3f58eSDavid du Colombier 
1840fb3f58eSDavid du Colombier 	ps->open = 0;
1850fb3f58eSDavid du Colombier 	ps->fd = fd;
1860fb3f58eSDavid du Colombier 	ps->input = input;
1870fb3f58eSDavid du Colombier 	if (fd < 0 || pipe(pifds) < 0)
1880fb3f58eSDavid du Colombier 		return -1;
1890fb3f58eSDavid du Colombier 	ps->kid = fork();
1900fb3f58eSDavid du Colombier 	switch (ps->kid) {
1910fb3f58eSDavid du Colombier 	case -1:
1920fb3f58eSDavid du Colombier 		return -1;
1930fb3f58eSDavid du Colombier 	case 0:
1940fb3f58eSDavid du Colombier 		if (input)
1950fb3f58eSDavid du Colombier 			dup(pifds[Wr], Stdout);
1960fb3f58eSDavid du Colombier 		else
1970fb3f58eSDavid du Colombier 			dup(pifds[Rd], Stdin);
1980fb3f58eSDavid du Colombier 		close(pifds[input? Rd: Wr]);
1990fb3f58eSDavid du Colombier 		dup(fd, (input? Stdin: Stdout));
2000fb3f58eSDavid du Colombier 		s = s_new();
2010fb3f58eSDavid du Colombier 		if (cmd[0] != '/')
2020fb3f58eSDavid du Colombier 			s_append(s, "/bin/");
2030fb3f58eSDavid du Colombier 		s_append(s, cmd);
2040fb3f58eSDavid du Colombier 		execl(s_to_c(s), cmd, nil);
2050fb3f58eSDavid du Colombier 		sysfatal("can't exec %s: %r", cmd);
2060fb3f58eSDavid du Colombier 	default:
2070fb3f58eSDavid du Colombier 		nfd = pifds[input? Rd: Wr];
2080fb3f58eSDavid du Colombier 		close(pifds[input? Wr: Rd]);
2090fb3f58eSDavid du Colombier 		break;
2100fb3f58eSDavid du Colombier 	}
2110fb3f58eSDavid du Colombier 	ps->rfd = nfd;
2120fb3f58eSDavid du Colombier 	ps->open = 1;
2130fb3f58eSDavid du Colombier 	return nfd;
2140fb3f58eSDavid du Colombier }
2150fb3f58eSDavid du Colombier 
2160fb3f58eSDavid du Colombier static char *
2170fb3f58eSDavid du Colombier pushclose(Pushstate *ps)
2180fb3f58eSDavid du Colombier {
2190fb3f58eSDavid du Colombier 	Waitmsg *wm;
2200fb3f58eSDavid du Colombier 
2210fb3f58eSDavid du Colombier 	if (ps->fd < 0 || ps->rfd < 0 || !ps->open)
2220fb3f58eSDavid du Colombier 		return "not open";
2230fb3f58eSDavid du Colombier 	close(ps->rfd);
2240fb3f58eSDavid du Colombier 	ps->rfd = -1;
2250fb3f58eSDavid du Colombier 	ps->open = 0;
2260fb3f58eSDavid du Colombier 	while ((wm = wait()) != nil && wm->pid != ps->kid)
2270fb3f58eSDavid du Colombier 		continue;
2280fb3f58eSDavid du Colombier 	return wm? wm->msg: nil;
2290fb3f58eSDavid du Colombier }
2300fb3f58eSDavid du Colombier 
2310fb3f58eSDavid du Colombier /*
2320fb3f58eSDavid du Colombier  * block-buffer management
2330fb3f58eSDavid du Colombier  */
2340fb3f58eSDavid du Colombier 
2350fb3f58eSDavid du Colombier static void
2360fb3f58eSDavid du Colombier initblks(void)
2370fb3f58eSDavid du Colombier {
2380fb3f58eSDavid du Colombier 	free(tpblk);
2390fb3f58eSDavid du Colombier 	tpblk = malloc(Tblock * nblock);
2400fb3f58eSDavid du Colombier 	assert(tpblk != nil);
2410fb3f58eSDavid du Colombier 	endblk = tpblk + nblock;
2420fb3f58eSDavid du Colombier }
2430fb3f58eSDavid du Colombier 
2444f281771SDavid du Colombier /*
2454f281771SDavid du Colombier  * (re)fill block buffers from archive.  `justhdr' means we don't care
2464f281771SDavid du Colombier  * about the data before the next header block.
2474f281771SDavid du Colombier  */
2480fb3f58eSDavid du Colombier static char *
2494f281771SDavid du Colombier refill(int ar, char *bufs, int justhdr)
2500fb3f58eSDavid du Colombier {
2510fb3f58eSDavid du Colombier 	int i, n;
2520fb3f58eSDavid du Colombier 	unsigned bytes = Tblock * nblock;
2534f281771SDavid du Colombier 	static int done, first = 1, seekable;
2540fb3f58eSDavid du Colombier 
2550fb3f58eSDavid du Colombier 	if (done)
2560fb3f58eSDavid du Colombier 		return nil;
2570fb3f58eSDavid du Colombier 
2584f281771SDavid du Colombier 	if (first)
2594f281771SDavid du Colombier 		seekable = seek(ar, 0, 1) >= 0;
26026ad7229SDavid du Colombier 	blkoff = seek(ar, 0, 1);		/* note position for `tar r' */
2610fb3f58eSDavid du Colombier 	/* try to size non-pipe input at first read */
2620fb3f58eSDavid du Colombier 	if (first && usefile) {
2630fb3f58eSDavid du Colombier 		n = read(ar, bufs, bytes);
2640fb3f58eSDavid du Colombier 		if (n <= 0)
2650fb3f58eSDavid du Colombier 			sysfatal("error reading archive: %r");
2660fb3f58eSDavid du Colombier 		i = n;
2670fb3f58eSDavid du Colombier 		if (i % Tblock != 0) {
2680fb3f58eSDavid du Colombier 			fprint(2, "%s: archive block size (%d) error\n",
2690fb3f58eSDavid du Colombier 				argv0, i);
2700fb3f58eSDavid du Colombier 			exits("blocksize");
2710fb3f58eSDavid du Colombier 		}
2720fb3f58eSDavid du Colombier 		i /= Tblock;
2730fb3f58eSDavid du Colombier 		if (i != nblock) {
2740fb3f58eSDavid du Colombier 			nblock = i;
2750fb3f58eSDavid du Colombier 			fprint(2, "%s: blocking = %d\n", argv0, nblock);
2760fb3f58eSDavid du Colombier 			endblk = (Hdr *)bufs + nblock;
2770fb3f58eSDavid du Colombier 			bytes = n;
2780fb3f58eSDavid du Colombier 		}
2794f281771SDavid du Colombier 	} else if (justhdr && seekable && nexthdr - seek(ar, 0, 1) >= bytes) {
2804f281771SDavid du Colombier 		/* optimisation for huge archive members on seekable media */
2814f281771SDavid du Colombier 		if (seek(ar, bytes, 1) < 0)
2824f281771SDavid du Colombier 			sysfatal("can't seek on archive: %r");
2834f281771SDavid du Colombier 		n = bytes;
2840fb3f58eSDavid du Colombier 	} else
2850fb3f58eSDavid du Colombier 		n = readn(ar, bufs, bytes);
2864f281771SDavid du Colombier 	first = 0;
2874f281771SDavid du Colombier 
2880fb3f58eSDavid du Colombier 	if (n == 0)
2890fb3f58eSDavid du Colombier 		sysfatal("unexpected EOF reading archive");
2900fb3f58eSDavid du Colombier 	else if (n < 0)
2910fb3f58eSDavid du Colombier 		sysfatal("error reading archive: %r");
2920fb3f58eSDavid du Colombier 	else if (n%Tblock != 0)
2930fb3f58eSDavid du Colombier 		sysfatal("partial block read from archive");
2940fb3f58eSDavid du Colombier 	if (n != bytes) {
2950fb3f58eSDavid du Colombier 		done = 1;
2960fb3f58eSDavid du Colombier 		memset(bufs + n, 0, bytes - n);
2970fb3f58eSDavid du Colombier 	}
2980fb3f58eSDavid du Colombier 	return bufs;
2990fb3f58eSDavid du Colombier }
3000fb3f58eSDavid du Colombier 
3010fb3f58eSDavid du Colombier static Hdr *
3024f281771SDavid du Colombier getblk(int ar, Refill rfp, int justhdr)
3030fb3f58eSDavid du Colombier {
3040fb3f58eSDavid du Colombier 	if (curblk == nil || curblk >= endblk) {  /* input block exhausted? */
3054f281771SDavid du Colombier 		if (rfp != nil && (*rfp)(ar, (char *)tpblk, justhdr) == nil)
3060fb3f58eSDavid du Colombier 			return nil;
3070fb3f58eSDavid du Colombier 		curblk = tpblk;
3080fb3f58eSDavid du Colombier 	}
3090fb3f58eSDavid du Colombier 	return curblk++;
3100fb3f58eSDavid du Colombier }
3110fb3f58eSDavid du Colombier 
3120fb3f58eSDavid du Colombier static Hdr *
3134f281771SDavid du Colombier getblkrd(int ar, int justhdr)
3140fb3f58eSDavid du Colombier {
3154f281771SDavid du Colombier 	return getblk(ar, refill, justhdr);
3160fb3f58eSDavid du Colombier }
3170fb3f58eSDavid du Colombier 
3180fb3f58eSDavid du Colombier static Hdr *
3190fb3f58eSDavid du Colombier getblke(int ar)
3200fb3f58eSDavid du Colombier {
3214f281771SDavid du Colombier 	return getblk(ar, nil, Alldata);
3220fb3f58eSDavid du Colombier }
3230fb3f58eSDavid du Colombier 
3240fb3f58eSDavid du Colombier static Hdr *
3250fb3f58eSDavid du Colombier getblkz(int ar)
3260fb3f58eSDavid du Colombier {
3270fb3f58eSDavid du Colombier 	Hdr *hp = getblke(ar);
3280fb3f58eSDavid du Colombier 
3290fb3f58eSDavid du Colombier 	if (hp != nil)
3300fb3f58eSDavid du Colombier 		memset(hp->data, 0, Tblock);
3310fb3f58eSDavid du Colombier 	return hp;
3320fb3f58eSDavid du Colombier }
3330fb3f58eSDavid du Colombier 
3340fb3f58eSDavid du Colombier /*
3350fb3f58eSDavid du Colombier  * how many block buffers are available, starting at the address
3360fb3f58eSDavid du Colombier  * just returned by getblk*?
3370fb3f58eSDavid du Colombier  */
3380fb3f58eSDavid du Colombier static int
3390fb3f58eSDavid du Colombier gothowmany(int max)
3400fb3f58eSDavid du Colombier {
3410fb3f58eSDavid du Colombier 	int n = endblk - (curblk - 1);
3420fb3f58eSDavid du Colombier 
3430fb3f58eSDavid du Colombier 	return n > max? max: n;
3440fb3f58eSDavid du Colombier }
3450fb3f58eSDavid du Colombier 
3460fb3f58eSDavid du Colombier /*
3470fb3f58eSDavid du Colombier  * indicate that one is done with the last block obtained from getblke
3480fb3f58eSDavid du Colombier  * and it is now available to be written into the archive.
3490fb3f58eSDavid du Colombier  */
3500fb3f58eSDavid du Colombier static void
3510fb3f58eSDavid du Colombier putlastblk(int ar)
3520fb3f58eSDavid du Colombier {
3530fb3f58eSDavid du Colombier 	unsigned bytes = Tblock * nblock;
3540fb3f58eSDavid du Colombier 
3550fb3f58eSDavid du Colombier 	/* if writing end-of-archive, aid compression (good hygiene too) */
3560fb3f58eSDavid du Colombier 	if (curblk < endblk)
3570fb3f58eSDavid du Colombier 		memset(curblk, 0, (char *)endblk - (char *)curblk);
3580fb3f58eSDavid du Colombier 	if (write(ar, tpblk, bytes) != bytes)
3590fb3f58eSDavid du Colombier 		sysfatal("error writing archive: %r");
36040ef9009SDavid du Colombier }
36140ef9009SDavid du Colombier 
36240ef9009SDavid du Colombier static void
3630fb3f58eSDavid du Colombier putblk(int ar)
36440ef9009SDavid du Colombier {
3650fb3f58eSDavid du Colombier 	if (curblk >= endblk)
3660fb3f58eSDavid du Colombier 		putlastblk(ar);
3670fb3f58eSDavid du Colombier }
3680fb3f58eSDavid du Colombier 
3690fb3f58eSDavid du Colombier static void
3700fb3f58eSDavid du Colombier putbackblk(int ar)
3710fb3f58eSDavid du Colombier {
3720fb3f58eSDavid du Colombier 	curblk--;
3730fb3f58eSDavid du Colombier 	USED(ar);
3740fb3f58eSDavid du Colombier }
3750fb3f58eSDavid du Colombier 
3760fb3f58eSDavid du Colombier static void
3770fb3f58eSDavid du Colombier putreadblks(int ar, int blks)
3780fb3f58eSDavid du Colombier {
3790fb3f58eSDavid du Colombier 	curblk += blks - 1;
3800fb3f58eSDavid du Colombier 	USED(ar);
3810fb3f58eSDavid du Colombier }
3820fb3f58eSDavid du Colombier 
3830fb3f58eSDavid du Colombier static void
3840fb3f58eSDavid du Colombier putblkmany(int ar, int blks)
3850fb3f58eSDavid du Colombier {
3860fb3f58eSDavid du Colombier 	curblk += blks - 1;
3870fb3f58eSDavid du Colombier 	putblk(ar);
3880fb3f58eSDavid du Colombier }
3890fb3f58eSDavid du Colombier 
3900fb3f58eSDavid du Colombier /*
3910fb3f58eSDavid du Colombier  * common routines
3920fb3f58eSDavid du Colombier  */
3930fb3f58eSDavid du Colombier 
3943366099cSDavid du Colombier /*
3953366099cSDavid du Colombier  * modifies hp->chksum but restores it; important for the last block of the
3963366099cSDavid du Colombier  * old archive when updating with `tar rf archive'
3973366099cSDavid du Colombier  */
398bbd061d4SDavid du Colombier static long
3990fb3f58eSDavid du Colombier chksum(Hdr *hp)
4000fb3f58eSDavid du Colombier {
4010fb3f58eSDavid du Colombier 	int n = Tblock;
4020fb3f58eSDavid du Colombier 	long i = 0;
4030fb3f58eSDavid du Colombier 	uchar *cp = hp->data;
4043366099cSDavid du Colombier 	char oldsum[sizeof hp->chksum];
4050fb3f58eSDavid du Colombier 
4063366099cSDavid du Colombier 	memmove(oldsum, hp->chksum, sizeof oldsum);
4070fb3f58eSDavid du Colombier 	memset(hp->chksum, ' ', sizeof hp->chksum);
4080fb3f58eSDavid du Colombier 	while (n-- > 0)
4090fb3f58eSDavid du Colombier 		i += *cp++;
4103366099cSDavid du Colombier 	memmove(hp->chksum, oldsum, sizeof oldsum);
4110fb3f58eSDavid du Colombier 	return i;
4120fb3f58eSDavid du Colombier }
4130fb3f58eSDavid du Colombier 
4140fb3f58eSDavid du Colombier static int
4150fb3f58eSDavid du Colombier isustar(Hdr *hp)
4160fb3f58eSDavid du Colombier {
4170fb3f58eSDavid du Colombier 	return strcmp(hp->magic, "ustar") == 0;
41840ef9009SDavid du Colombier }
41940ef9009SDavid du Colombier 
42040ef9009SDavid du Colombier /*
42140ef9009SDavid du Colombier  * s is at most n bytes long, but need not be NUL-terminated.
42240ef9009SDavid du Colombier  * if shorter than n bytes, all bytes after the first NUL must also
42340ef9009SDavid du Colombier  * be NUL.
42440ef9009SDavid du Colombier  */
42540ef9009SDavid du Colombier static int
42640ef9009SDavid du Colombier strnlen(char *s, int n)
42740ef9009SDavid du Colombier {
4280fb3f58eSDavid du Colombier 	return s[n - 1] != '\0'? n: strlen(s);
42940ef9009SDavid du Colombier }
43040ef9009SDavid du Colombier 
4310fb3f58eSDavid du Colombier /* set fullname from header */
4320fb3f58eSDavid du Colombier static char *
4330fb3f58eSDavid du Colombier name(Hdr *hp)
43440ef9009SDavid du Colombier {
43540ef9009SDavid du Colombier 	int pfxlen, namlen;
43623605fa3SDavid du Colombier 	static char fullnamebuf[2 + Maxname + 1];	/* 2 at beginning for ./ on relative names */
43723605fa3SDavid du Colombier 	char *fullname;
43840ef9009SDavid du Colombier 
43923605fa3SDavid du Colombier 	fullname = fullnamebuf+2;
44040ef9009SDavid du Colombier 	namlen = strnlen(hp->name, sizeof hp->name);
4410fb3f58eSDavid du Colombier 	if (hp->prefix[0] == '\0' || !isustar(hp)) {	/* old-style name? */
44240ef9009SDavid du Colombier 		memmove(fullname, hp->name, namlen);
44340ef9009SDavid du Colombier 		fullname[namlen] = '\0';
4440fb3f58eSDavid du Colombier 		return fullname;
44540ef9009SDavid du Colombier 	}
4460fb3f58eSDavid du Colombier 
4470fb3f58eSDavid du Colombier 	/* name is in two pieces */
44840ef9009SDavid du Colombier 	pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
44940ef9009SDavid du Colombier 	memmove(fullname, hp->prefix, pfxlen);
45040ef9009SDavid du Colombier 	fullname[pfxlen] = '/';
45140ef9009SDavid du Colombier 	memmove(fullname + pfxlen + 1, hp->name, namlen);
45240ef9009SDavid du Colombier 	fullname[pfxlen + 1 + namlen] = '\0';
4530fb3f58eSDavid du Colombier 	return fullname;
4540fb3f58eSDavid du Colombier }
4550fb3f58eSDavid du Colombier 
4560fb3f58eSDavid du Colombier static int
4570fb3f58eSDavid du Colombier isdir(Hdr *hp)
4580fb3f58eSDavid du Colombier {
4590fb3f58eSDavid du Colombier 	/* the mode test is ugly but sometimes necessary */
4600fb3f58eSDavid du Colombier 	return hp->linkflag == LF_DIR ||
4610fb3f58eSDavid du Colombier 		strrchr(name(hp), '\0')[-1] == '/' ||
4620fb3f58eSDavid du Colombier 		(strtoul(hp->mode, nil, 8)&0170000) == 040000;
4630fb3f58eSDavid du Colombier }
4640fb3f58eSDavid du Colombier 
4650fb3f58eSDavid du Colombier static int
4660fb3f58eSDavid du Colombier eotar(Hdr *hp)
4670fb3f58eSDavid du Colombier {
4680fb3f58eSDavid du Colombier 	return name(hp)[0] == '\0';
4690fb3f58eSDavid du Colombier }
4700fb3f58eSDavid du Colombier 
471bbd061d4SDavid du Colombier /*
472bbd061d4SDavid du Colombier static uvlong
473bbd061d4SDavid du Colombier getbe(uchar *src, int size)
474bbd061d4SDavid du Colombier {
475bbd061d4SDavid du Colombier 	uvlong vl = 0;
476bbd061d4SDavid du Colombier 
477bbd061d4SDavid du Colombier 	while (size-- > 0) {
478bbd061d4SDavid du Colombier 		vl <<= 8;
479bbd061d4SDavid du Colombier 		vl |= *src++;
480bbd061d4SDavid du Colombier 	}
481bbd061d4SDavid du Colombier 	return vl;
482bbd061d4SDavid du Colombier }
483bbd061d4SDavid du Colombier  */
484bbd061d4SDavid du Colombier 
485bbd061d4SDavid du Colombier static void
486bbd061d4SDavid du Colombier putbe(uchar *dest, uvlong vl, int size)
487bbd061d4SDavid du Colombier {
488bbd061d4SDavid du Colombier 	for (dest += size; size-- > 0; vl >>= 8)
489bbd061d4SDavid du Colombier 		*--dest = vl;
490bbd061d4SDavid du Colombier }
491bbd061d4SDavid du Colombier 
492bbd061d4SDavid du Colombier /*
493bbd061d4SDavid du Colombier  * return the nominal size from the header block, which is not always the
494bbd061d4SDavid du Colombier  * size in the archive (the archive size may be zero for some file types
495bbd061d4SDavid du Colombier  * regardless of the nominal size).
496bbd061d4SDavid du Colombier  *
497bbd061d4SDavid du Colombier  * gnu and freebsd tars are now recording vlongs as big-endian binary
498bbd061d4SDavid du Colombier  * with a flag in byte 0 to indicate this, which permits file sizes up to
499bbd061d4SDavid du Colombier  * 2^64-1 (actually 2^80-1 but our file sizes are vlongs) rather than 2^33-1.
500bbd061d4SDavid du Colombier  */
501bbd061d4SDavid du Colombier static Off
5024f281771SDavid du Colombier hdrsize(Hdr *hp)
5034f281771SDavid du Colombier {
504bbd061d4SDavid du Colombier 	uchar *p;
5054f281771SDavid du Colombier 
506bbd061d4SDavid du Colombier 	if((uchar)hp->size[0] == Binnegsz) {
507bbd061d4SDavid du Colombier 		fprint(2, "%s: %s: negative length, which is insane\n",
508bbd061d4SDavid du Colombier 			argv0, name(hp));
509bbd061d4SDavid du Colombier 		return 0;
510bbd061d4SDavid du Colombier 	} else if((uchar)hp->size[0] == Binsize) {
511bbd061d4SDavid du Colombier 		p = (uchar *)hp->size + sizeof hp->size - 1 -
512bbd061d4SDavid du Colombier 			sizeof(vlong);		/* -1 for terminating space */
513bbd061d4SDavid du Colombier 		return G8BEBYTE(p);
514bbd061d4SDavid du Colombier 	} else
515bbd061d4SDavid du Colombier 		return strtoull(hp->size, nil, 8);
516bbd061d4SDavid du Colombier }
517bbd061d4SDavid du Colombier 
518bbd061d4SDavid du Colombier /*
519bbd061d4SDavid du Colombier  * return the number of bytes recorded in the archive.
520bbd061d4SDavid du Colombier  */
521bbd061d4SDavid du Colombier static Off
522bbd061d4SDavid du Colombier arsize(Hdr *hp)
523bbd061d4SDavid du Colombier {
5245437ee90SDavid du Colombier 	if(isdir(hp) || islink(hp->linkflag))
525bbd061d4SDavid du Colombier 		return 0;
526bbd061d4SDavid du Colombier 	return hdrsize(hp);
5274f281771SDavid du Colombier }
5284f281771SDavid du Colombier 
5290fb3f58eSDavid du Colombier static Hdr *
5300fb3f58eSDavid du Colombier readhdr(int ar)
5310fb3f58eSDavid du Colombier {
5320fb3f58eSDavid du Colombier 	long hdrcksum;
5330fb3f58eSDavid du Colombier 	Hdr *hp;
5340fb3f58eSDavid du Colombier 
5354f281771SDavid du Colombier 	hp = getblkrd(ar, Alldata);
5360fb3f58eSDavid du Colombier 	if (hp == nil)
5370fb3f58eSDavid du Colombier 		sysfatal("unexpected EOF instead of archive header");
5380fb3f58eSDavid du Colombier 	if (eotar(hp))			/* end-of-archive block? */
5390fb3f58eSDavid du Colombier 		return nil;
5400fb3f58eSDavid du Colombier 	hdrcksum = strtoul(hp->chksum, nil, 8);
5410fb3f58eSDavid du Colombier 	if (chksum(hp) != hdrcksum)
5420fb3f58eSDavid du Colombier 		sysfatal("bad archive header checksum: name %.64s...",
5430fb3f58eSDavid du Colombier 			hp->name);
544bbd061d4SDavid du Colombier 	nexthdr += Tblock*(1 + BYTES2TBLKS(arsize(hp)));
5450fb3f58eSDavid du Colombier 	return hp;
54640ef9009SDavid du Colombier }
54740ef9009SDavid du Colombier 
54840ef9009SDavid du Colombier /*
5490fb3f58eSDavid du Colombier  * tar r[c]
5500fb3f58eSDavid du Colombier  */
5510fb3f58eSDavid du Colombier 
5520fb3f58eSDavid du Colombier /*
5530fb3f58eSDavid du Colombier  * if name is longer than Namsiz bytes, try to split it at a slash and fit the
55440ef9009SDavid du Colombier  * pieces into hp->prefix and hp->name.
55540ef9009SDavid du Colombier  */
55640ef9009SDavid du Colombier static int
5570fb3f58eSDavid du Colombier putfullname(Hdr *hp, char *name)
55840ef9009SDavid du Colombier {
55940ef9009SDavid du Colombier 	int namlen, pfxlen;
56040ef9009SDavid du Colombier 	char *sl, *osl;
5610fb3f58eSDavid du Colombier 	String *slname = nil;
5620fb3f58eSDavid du Colombier 
5630fb3f58eSDavid du Colombier 	if (isdir(hp)) {
5640fb3f58eSDavid du Colombier 		slname = s_new();
5650fb3f58eSDavid du Colombier 		s_append(slname, name);
5660fb3f58eSDavid du Colombier 		s_append(slname, "/");		/* posix requires this */
5670fb3f58eSDavid du Colombier 		name = s_to_c(slname);
5680fb3f58eSDavid du Colombier 	}
56940ef9009SDavid du Colombier 
57040ef9009SDavid du Colombier 	namlen = strlen(name);
5710fb3f58eSDavid du Colombier 	if (namlen <= Namsiz) {
5720fb3f58eSDavid du Colombier 		strncpy(hp->name, name, Namsiz);
57340ef9009SDavid du Colombier 		hp->prefix[0] = '\0';		/* ustar paranoia */
57440ef9009SDavid du Colombier 		return 0;
57540ef9009SDavid du Colombier 	}
5760fb3f58eSDavid du Colombier 
5770fb3f58eSDavid du Colombier 	if (!posix || namlen > Maxname) {
5780fb3f58eSDavid du Colombier 		fprint(2, "%s: name too long for tar header: %s\n",
5790fb3f58eSDavid du Colombier 			argv0, name);
58040ef9009SDavid du Colombier 		return -1;
58140ef9009SDavid du Colombier 	}
58240ef9009SDavid du Colombier 	/*
58340ef9009SDavid du Colombier 	 * try various splits until one results in pieces that fit into the
58440ef9009SDavid du Colombier 	 * appropriate fields of the header.  look for slashes from right
58540ef9009SDavid du Colombier 	 * to left, in the hopes of putting the largest part of the name into
58640ef9009SDavid du Colombier 	 * hp->prefix, which is larger than hp->name.
58740ef9009SDavid du Colombier 	 */
58840ef9009SDavid du Colombier 	sl = strrchr(name, '/');
58940ef9009SDavid du Colombier 	while (sl != nil) {
59040ef9009SDavid du Colombier 		pfxlen = sl - name;
5910fb3f58eSDavid du Colombier 		if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz)
59240ef9009SDavid du Colombier 			break;
59340ef9009SDavid du Colombier 		osl = sl;
59440ef9009SDavid du Colombier 		*osl = '\0';
59540ef9009SDavid du Colombier 		sl = strrchr(name, '/');
59640ef9009SDavid du Colombier 		*osl = '/';
59740ef9009SDavid du Colombier 	}
59840ef9009SDavid du Colombier 	if (sl == nil) {
5990fb3f58eSDavid du Colombier 		fprint(2, "%s: name can't be split to fit tar header: %s\n",
6000fb3f58eSDavid du Colombier 			argv0, name);
60140ef9009SDavid du Colombier 		return -1;
60240ef9009SDavid du Colombier 	}
60340ef9009SDavid du Colombier 	*sl = '\0';
60440ef9009SDavid du Colombier 	strncpy(hp->prefix, name, sizeof hp->prefix);
6050fb3f58eSDavid du Colombier 	*sl++ = '/';
6060fb3f58eSDavid du Colombier 	strncpy(hp->name, sl, sizeof hp->name);
6070fb3f58eSDavid du Colombier 	if (slname)
6080fb3f58eSDavid du Colombier 		s_free(slname);
60940ef9009SDavid du Colombier 	return 0;
61040ef9009SDavid du Colombier }
61140ef9009SDavid du Colombier 
6120fb3f58eSDavid du Colombier static int
6130fb3f58eSDavid du Colombier mkhdr(Hdr *hp, Dir *dir, char *file)
6143e12c5d1SDavid du Colombier {
6150fb3f58eSDavid du Colombier 	/*
6160fb3f58eSDavid du Colombier 	 * these fields run together, so we format them in order and don't use
6170fb3f58eSDavid du Colombier 	 * snprint.
6180fb3f58eSDavid du Colombier 	 */
6190fb3f58eSDavid du Colombier 	sprint(hp->mode, "%6lo ", dir->mode & 0777);
6200fb3f58eSDavid du Colombier 	sprint(hp->uid, "%6o ", aruid);
6210fb3f58eSDavid du Colombier 	sprint(hp->gid, "%6o ", argid);
622bbd061d4SDavid du Colombier 	if (dir->length >= (Off)1<<32) {
623bbd061d4SDavid du Colombier 		static int printed;
624bbd061d4SDavid du Colombier 
625bbd061d4SDavid du Colombier 		if (!printed) {
626bbd061d4SDavid du Colombier 			printed = 1;
627bbd061d4SDavid du Colombier 			fprint(2, "%s: storing large sizes in \"base 256\"\n", argv0);
6280fb3f58eSDavid du Colombier 		}
629bbd061d4SDavid du Colombier 		hp->size[0] = Binsize;
630bbd061d4SDavid du Colombier 		/* emit so-called `base 256' representation of size */
631bbd061d4SDavid du Colombier 		putbe((uchar *)hp->size+1, dir->length, sizeof hp->size - 2);
632bbd061d4SDavid du Colombier 		hp->size[sizeof hp->size - 1] = ' ';
633bbd061d4SDavid du Colombier 	} else
6340fb3f58eSDavid du Colombier 		sprint(hp->size, "%11lluo ", dir->length);
6350fb3f58eSDavid du Colombier 	sprint(hp->mtime, "%11luo ", dir->mtime);
6360fb3f58eSDavid du Colombier 	hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1);
6370fb3f58eSDavid du Colombier 	putfullname(hp, file);
6380fb3f58eSDavid du Colombier 	if (posix) {
6390fb3f58eSDavid du Colombier 		strncpy(hp->magic, "ustar", sizeof hp->magic);
6400fb3f58eSDavid du Colombier 		strncpy(hp->version, "00", sizeof hp->version);
6410fb3f58eSDavid du Colombier 		strncpy(hp->uname, dir->uid, sizeof hp->uname);
6420fb3f58eSDavid du Colombier 		strncpy(hp->gname, dir->gid, sizeof hp->gname);
6430fb3f58eSDavid du Colombier 	}
6440fb3f58eSDavid du Colombier 	sprint(hp->chksum, "%6luo", chksum(hp));
6450fb3f58eSDavid du Colombier 	return 0;
6463e12c5d1SDavid du Colombier }
6473e12c5d1SDavid du Colombier 
6480fb3f58eSDavid du Colombier static void addtoar(int ar, char *file, char *shortf);
6493e12c5d1SDavid du Colombier 
6500fb3f58eSDavid du Colombier static void
6510fb3f58eSDavid du Colombier addtreetoar(int ar, char *file, char *shortf, int fd)
6523e12c5d1SDavid du Colombier {
6530fb3f58eSDavid du Colombier 	int n;
6540fb3f58eSDavid du Colombier 	Dir *dent, *dirents;
6550fb3f58eSDavid du Colombier 	String *name = s_new();
6563e12c5d1SDavid du Colombier 
6570fb3f58eSDavid du Colombier 	n = dirreadall(fd, &dirents);
6580fb3f58eSDavid du Colombier 	close(fd);
6590fb3f58eSDavid du Colombier 	if (n == 0)
6603e12c5d1SDavid du Colombier 		return;
6610fb3f58eSDavid du Colombier 
6620fb3f58eSDavid du Colombier 	if (chdir(shortf) < 0)
6630fb3f58eSDavid du Colombier 		sysfatal("chdir %s: %r", file);
6640fb3f58eSDavid du Colombier 	if (DEBUG)
6650fb3f58eSDavid du Colombier 		fprint(2, "chdir %s\t# %s\n", shortf, file);
6660fb3f58eSDavid du Colombier 
6670fb3f58eSDavid du Colombier 	for (dent = dirents; dent < dirents + n; dent++) {
6680fb3f58eSDavid du Colombier 		s_reset(name);
6690fb3f58eSDavid du Colombier 		s_append(name, file);
6700fb3f58eSDavid du Colombier 		s_append(name, "/");
6710fb3f58eSDavid du Colombier 		s_append(name, dent->name);
6720fb3f58eSDavid du Colombier 		addtoar(ar, s_to_c(name), dent->name);
6739a747e4fSDavid du Colombier 	}
6740fb3f58eSDavid du Colombier 	s_free(name);
6750fb3f58eSDavid du Colombier 	free(dirents);
6760fb3f58eSDavid du Colombier 
677f3703655SDavid du Colombier 	/*
678f3703655SDavid du Colombier 	 * this assumes that shortf is just one component, which is true
679f3703655SDavid du Colombier 	 * during directory descent, but not necessarily true of command-line
680f3703655SDavid du Colombier 	 * arguments.  Our caller (or addtoar's) must reset the working
681f3703655SDavid du Colombier 	 * directory if necessary.
682f3703655SDavid du Colombier 	 */
6830fb3f58eSDavid du Colombier 	if (chdir("..") < 0)
6840fb3f58eSDavid du Colombier 		sysfatal("chdir %s/..: %r", file);
6850fb3f58eSDavid du Colombier 	if (DEBUG)
6860fb3f58eSDavid du Colombier 		fprint(2, "chdir ..\n");
6873e12c5d1SDavid du Colombier }
6883e12c5d1SDavid du Colombier 
6890fb3f58eSDavid du Colombier static void
6900fb3f58eSDavid du Colombier addtoar(int ar, char *file, char *shortf)
6913e12c5d1SDavid du Colombier {
6920fb3f58eSDavid du Colombier 	int n, fd, isdir;
6930fb3f58eSDavid du Colombier 	long bytes;
6940fb3f58eSDavid du Colombier 	ulong blksleft, blksread;
6950fb3f58eSDavid du Colombier 	Hdr *hbp;
6960fb3f58eSDavid du Colombier 	Dir *dir;
697859ebcadSDavid du Colombier 	String *name = nil;
698859ebcadSDavid du Colombier 
699859ebcadSDavid du Colombier 	if (shortf[0] == '#') {
700859ebcadSDavid du Colombier 		name = s_new();
701859ebcadSDavid du Colombier 		s_append(name, "./");
702859ebcadSDavid du Colombier 		s_append(name, shortf);
703859ebcadSDavid du Colombier 		shortf = s_to_c(name);
704859ebcadSDavid du Colombier 	}
7053e12c5d1SDavid du Colombier 
7060fb3f58eSDavid du Colombier 	fd = open(shortf, OREAD);
7070fb3f58eSDavid du Colombier 	if (fd < 0) {
7080fb3f58eSDavid du Colombier 		fprint(2, "%s: can't open %s: %r\n", argv0, file);
709859ebcadSDavid du Colombier 		if (name)
710859ebcadSDavid du Colombier 			s_free(name);
7110fb3f58eSDavid du Colombier 		return;
7120fb3f58eSDavid du Colombier 	}
7130fb3f58eSDavid du Colombier 	dir = dirfstat(fd);
7140fb3f58eSDavid du Colombier 	if (dir == nil)
7150fb3f58eSDavid du Colombier 		sysfatal("can't fstat %s: %r", file);
7160fb3f58eSDavid du Colombier 
7170fb3f58eSDavid du Colombier 	hbp = getblkz(ar);
7180fb3f58eSDavid du Colombier 	isdir = !!(dir->qid.type&QTDIR);
7190fb3f58eSDavid du Colombier 	if (mkhdr(hbp, dir, file) < 0) {
7200fb3f58eSDavid du Colombier 		putbackblk(ar);
7210fb3f58eSDavid du Colombier 		free(dir);
7220fb3f58eSDavid du Colombier 		close(fd);
723859ebcadSDavid du Colombier 		if (name)
724859ebcadSDavid du Colombier 			s_free(name);
7250fb3f58eSDavid du Colombier 		return;
7260fb3f58eSDavid du Colombier 	}
7270fb3f58eSDavid du Colombier 	putblk(ar);
7280fb3f58eSDavid du Colombier 
7290fb3f58eSDavid du Colombier 	blksleft = BYTES2TBLKS(dir->length);
7300fb3f58eSDavid du Colombier 	free(dir);
7310fb3f58eSDavid du Colombier 
7320fb3f58eSDavid du Colombier 	if (isdir)
7330fb3f58eSDavid du Colombier 		addtreetoar(ar, file, shortf, fd);
7340fb3f58eSDavid du Colombier 	else {
7350fb3f58eSDavid du Colombier 		for (; blksleft > 0; blksleft -= blksread) {
7360fb3f58eSDavid du Colombier 			hbp = getblke(ar);
7370fb3f58eSDavid du Colombier 			blksread = gothowmany(blksleft);
7380fb3f58eSDavid du Colombier 			bytes = blksread * Tblock;
7390fb3f58eSDavid du Colombier 			n = readn(fd, hbp->data, bytes);
7400fb3f58eSDavid du Colombier 			if (n < 0)
7410fb3f58eSDavid du Colombier 				sysfatal("error reading %s: %r", file);
7420fb3f58eSDavid du Colombier 			/*
7430fb3f58eSDavid du Colombier 			 * ignore EOF.  zero any partial block to aid
7440fb3f58eSDavid du Colombier 			 * compression and emergency recovery of data.
7450fb3f58eSDavid du Colombier 			 */
7460fb3f58eSDavid du Colombier 			if (n < Tblock)
7470fb3f58eSDavid du Colombier 				memset(hbp->data + n, 0, bytes - n);
7480fb3f58eSDavid du Colombier 			putblkmany(ar, blksread);
7490fb3f58eSDavid du Colombier 		}
7500fb3f58eSDavid du Colombier 		close(fd);
7510fb3f58eSDavid du Colombier 		if (verbose)
7520fb3f58eSDavid du Colombier 			fprint(2, "%s\n", file);
7530fb3f58eSDavid du Colombier 	}
754859ebcadSDavid du Colombier 	if (name)
755859ebcadSDavid du Colombier 		s_free(name);
7560fb3f58eSDavid du Colombier }
7570fb3f58eSDavid du Colombier 
7580fb3f58eSDavid du Colombier static char *
7590fb3f58eSDavid du Colombier replace(char **argv)
7600fb3f58eSDavid du Colombier {
7610fb3f58eSDavid du Colombier 	int i, ar;
7620fb3f58eSDavid du Colombier 	ulong blksleft, blksread;
7630fb3f58eSDavid du Colombier 	Off bytes;
7640fb3f58eSDavid du Colombier 	Hdr *hp;
7650fb3f58eSDavid du Colombier 	Compress *comp = nil;
7660fb3f58eSDavid du Colombier 	Pushstate ps;
7670fb3f58eSDavid du Colombier 
7680fb3f58eSDavid du Colombier 	if (usefile && docreate) {
7690fb3f58eSDavid du Colombier 		ar = create(usefile, OWRITE, 0666);
7700fb3f58eSDavid du Colombier 		if (docompress)
7710fb3f58eSDavid du Colombier 			comp = compmethod(usefile);
7720fb3f58eSDavid du Colombier 	} else if (usefile)
7730fb3f58eSDavid du Colombier 		ar = open(usefile, ORDWR);
7740fb3f58eSDavid du Colombier 	else
7750fb3f58eSDavid du Colombier 		ar = Stdout;
7760fb3f58eSDavid du Colombier 	if (comp)
7770fb3f58eSDavid du Colombier 		ar = push(ar, comp->comp, Output, &ps);
7780fb3f58eSDavid du Colombier 	if (ar < 0)
7790fb3f58eSDavid du Colombier 		sysfatal("can't open archive %s: %r", usefile);
7800fb3f58eSDavid du Colombier 
7810fb3f58eSDavid du Colombier 	if (usefile && !docreate) {
7820fb3f58eSDavid du Colombier 		/* skip quickly to the end */
7830fb3f58eSDavid du Colombier 		while ((hp = readhdr(ar)) != nil) {
784bbd061d4SDavid du Colombier 			bytes = arsize(hp);
7850fb3f58eSDavid du Colombier 			for (blksleft = BYTES2TBLKS(bytes);
7864f281771SDavid du Colombier 			     blksleft > 0 && getblkrd(ar, Justnxthdr) != nil;
7870fb3f58eSDavid du Colombier 			     blksleft -= blksread) {
7880fb3f58eSDavid du Colombier 				blksread = gothowmany(blksleft);
7890fb3f58eSDavid du Colombier 				putreadblks(ar, blksread);
7900fb3f58eSDavid du Colombier 			}
7910fb3f58eSDavid du Colombier 		}
7920fb3f58eSDavid du Colombier 		/*
7930fb3f58eSDavid du Colombier 		 * we have just read the end-of-archive Tblock.
7940fb3f58eSDavid du Colombier 		 * now seek back over the (big) archive block containing it,
7950fb3f58eSDavid du Colombier 		 * and back up curblk ptr over end-of-archive Tblock in memory.
7960fb3f58eSDavid du Colombier 		 */
7973366099cSDavid du Colombier 		if (seek(ar, blkoff, 0) < 0)
7980fb3f58eSDavid du Colombier 			sysfatal("can't seek back over end-of-archive: %r");
7990fb3f58eSDavid du Colombier 		curblk--;
8000fb3f58eSDavid du Colombier 	}
8010fb3f58eSDavid du Colombier 
802f3703655SDavid du Colombier 	for (i = 0; argv[i] != nil; i++) {
8030fb3f58eSDavid du Colombier 		addtoar(ar, argv[i], argv[i]);
804f3703655SDavid du Colombier 		chdir(origdir);		/* for correctness & profiling */
805f3703655SDavid du Colombier 	}
8060fb3f58eSDavid du Colombier 
8070fb3f58eSDavid du Colombier 	/* write end-of-archive marker */
8080fb3f58eSDavid du Colombier 	getblkz(ar);
8090fb3f58eSDavid du Colombier 	putblk(ar);
8100fb3f58eSDavid du Colombier 	getblkz(ar);
8110fb3f58eSDavid du Colombier 	putlastblk(ar);
8120fb3f58eSDavid du Colombier 
8130fb3f58eSDavid du Colombier 	if (comp)
8140fb3f58eSDavid du Colombier 		return pushclose(&ps);
8150fb3f58eSDavid du Colombier 	if (ar > Stderr)
8160fb3f58eSDavid du Colombier 		close(ar);
8170fb3f58eSDavid du Colombier 	return nil;
8180fb3f58eSDavid du Colombier }
8190fb3f58eSDavid du Colombier 
8200fb3f58eSDavid du Colombier /*
8210fb3f58eSDavid du Colombier  * tar [xt]
8220fb3f58eSDavid du Colombier  */
8230fb3f58eSDavid du Colombier 
8240fb3f58eSDavid du Colombier /* is pfx a file-name prefix of name? */
8250fb3f58eSDavid du Colombier static int
8260fb3f58eSDavid du Colombier prefix(char *name, char *pfx)
8270fb3f58eSDavid du Colombier {
8280fb3f58eSDavid du Colombier 	int pfxlen = strlen(pfx);
8290fb3f58eSDavid du Colombier 	char clpfx[Maxname+1];
8300fb3f58eSDavid du Colombier 
8310fb3f58eSDavid du Colombier 	if (pfxlen > Maxname)
8320fb3f58eSDavid du Colombier 		return 0;
8330fb3f58eSDavid du Colombier 	strcpy(clpfx, pfx);
8340fb3f58eSDavid du Colombier 	cleanname(clpfx);
8350fb3f58eSDavid du Colombier 	return strncmp(pfx, name, pfxlen) == 0 &&
8360fb3f58eSDavid du Colombier 		(name[pfxlen] == '\0' || name[pfxlen] == '/');
8370fb3f58eSDavid du Colombier }
8380fb3f58eSDavid du Colombier 
8390fb3f58eSDavid du Colombier static int
8400fb3f58eSDavid du Colombier match(char *name, char **argv)
8410fb3f58eSDavid du Colombier {
8420fb3f58eSDavid du Colombier 	int i;
8430fb3f58eSDavid du Colombier 	char clname[Maxname+1];
8440fb3f58eSDavid du Colombier 
8450fb3f58eSDavid du Colombier 	if (argv[0] == nil)
8460fb3f58eSDavid du Colombier 		return 1;
8470fb3f58eSDavid du Colombier 	strcpy(clname, name);
8480fb3f58eSDavid du Colombier 	cleanname(clname);
8490fb3f58eSDavid du Colombier 	for (i = 0; argv[i] != nil; i++)
8500fb3f58eSDavid du Colombier 		if (prefix(clname, argv[i]))
8510fb3f58eSDavid du Colombier 			return 1;
8520fb3f58eSDavid du Colombier 	return 0;
8530fb3f58eSDavid du Colombier }
8540fb3f58eSDavid du Colombier 
8558b6d9ba0SDavid du Colombier static void
8568b6d9ba0SDavid du Colombier cantcreate(char *s, int mode)
8578b6d9ba0SDavid du Colombier {
8588b6d9ba0SDavid du Colombier 	int len;
8598b6d9ba0SDavid du Colombier 	static char *last;
8608b6d9ba0SDavid du Colombier 
8618b6d9ba0SDavid du Colombier 	/*
8628b6d9ba0SDavid du Colombier 	 * Always print about files.  Only print about directories
8638b6d9ba0SDavid du Colombier 	 * we haven't printed about.  (Assumes archive is ordered
8648b6d9ba0SDavid du Colombier 	 * nicely.)
8658b6d9ba0SDavid du Colombier 	 */
8668b6d9ba0SDavid du Colombier 	if(mode&DMDIR){
8678b6d9ba0SDavid du Colombier 		if(last){
8688b6d9ba0SDavid du Colombier 			/* already printed this directory */
8698b6d9ba0SDavid du Colombier 			if(strcmp(s, last) == 0)
8708b6d9ba0SDavid du Colombier 				return;
8718b6d9ba0SDavid du Colombier 			/* printed a higher directory, so printed this one */
8728b6d9ba0SDavid du Colombier 			len = strlen(s);
8738b6d9ba0SDavid du Colombier 			if(memcmp(s, last, len) == 0 && last[len] == '/')
8748b6d9ba0SDavid du Colombier 				return;
8758b6d9ba0SDavid du Colombier 		}
8768b6d9ba0SDavid du Colombier 		/* save */
8778b6d9ba0SDavid du Colombier 		free(last);
8788b6d9ba0SDavid du Colombier 		last = strdup(s);
8798b6d9ba0SDavid du Colombier 	}
8808b6d9ba0SDavid du Colombier 	fprint(2, "%s: can't create %s: %r\n", argv0, s);
8818b6d9ba0SDavid du Colombier }
8828b6d9ba0SDavid du Colombier 
8830fb3f58eSDavid du Colombier static int
8840fb3f58eSDavid du Colombier makedir(char *s)
8850fb3f58eSDavid du Colombier {
8860fb3f58eSDavid du Colombier 	int f;
8870fb3f58eSDavid du Colombier 
8880fb3f58eSDavid du Colombier 	if (access(s, AEXIST) == 0)
8890fb3f58eSDavid du Colombier 		return -1;
8900fb3f58eSDavid du Colombier 	f = create(s, OREAD, DMDIR | 0777);
8910fb3f58eSDavid du Colombier 	if (f >= 0)
8920fb3f58eSDavid du Colombier 		close(f);
8938b6d9ba0SDavid du Colombier 	else
8948b6d9ba0SDavid du Colombier 		cantcreate(s, DMDIR);
8950fb3f58eSDavid du Colombier 	return f;
8960fb3f58eSDavid du Colombier }
8970fb3f58eSDavid du Colombier 
8988b6d9ba0SDavid du Colombier static int
8990fb3f58eSDavid du Colombier mkpdirs(char *s)
9000fb3f58eSDavid du Colombier {
9018b6d9ba0SDavid du Colombier 	int err;
9028b6d9ba0SDavid du Colombier 	char *p;
9030fb3f58eSDavid du Colombier 
9048b6d9ba0SDavid du Colombier 	p = s;
9058b6d9ba0SDavid du Colombier 	err = 0;
9068b6d9ba0SDavid du Colombier 	while (!err && (p = strchr(p+1, '/')) != nil) {
9070fb3f58eSDavid du Colombier 		*p = '\0';
9088b6d9ba0SDavid du Colombier 		err = (access(s, AEXIST) < 0 && makedir(s) < 0);
9090fb3f58eSDavid du Colombier 		*p = '/';
9100fb3f58eSDavid du Colombier 	}
9118b6d9ba0SDavid du Colombier 	return -err;
9120fb3f58eSDavid du Colombier }
9130fb3f58eSDavid du Colombier 
914312a1df1SDavid du Colombier /* Call access but preserve the error string. */
915312a1df1SDavid du Colombier static int
916312a1df1SDavid du Colombier xaccess(char *name, int mode)
917312a1df1SDavid du Colombier {
918312a1df1SDavid du Colombier 	char err[ERRMAX];
919312a1df1SDavid du Colombier 	int rv;
920312a1df1SDavid du Colombier 
921312a1df1SDavid du Colombier 	err[0] = 0;
922312a1df1SDavid du Colombier 	errstr(err, sizeof err);
923312a1df1SDavid du Colombier 	rv = access(name, mode);
924312a1df1SDavid du Colombier 	errstr(err, sizeof err);
925312a1df1SDavid du Colombier 	return rv;
926312a1df1SDavid du Colombier }
927312a1df1SDavid du Colombier 
9280fb3f58eSDavid du Colombier /* copy a file from the archive into the filesystem */
92923605fa3SDavid du Colombier /* fname is result of name(), so has two extra bytes at beginning */
9300fb3f58eSDavid du Colombier static void
9310fb3f58eSDavid du Colombier extract1(int ar, Hdr *hp, char *fname)
9320fb3f58eSDavid du Colombier {
9330fb3f58eSDavid du Colombier 	int wrbytes, fd = -1, dir = 0;
9340fb3f58eSDavid du Colombier 	long mtime = strtol(hp->mtime, nil, 8);
9350fb3f58eSDavid du Colombier 	ulong mode = strtoul(hp->mode, nil, 8) & 0777;
936bbd061d4SDavid du Colombier 	Off bytes = hdrsize(hp);		/* for printing */
937bbd061d4SDavid du Colombier 	ulong blksread, blksleft = BYTES2TBLKS(arsize(hp));
9380fb3f58eSDavid du Colombier 	Hdr *hbp;
9390fb3f58eSDavid du Colombier 
9400fb3f58eSDavid du Colombier 	if (isdir(hp)) {
9410fb3f58eSDavid du Colombier 		mode |= DMDIR|0700;
9420fb3f58eSDavid du Colombier 		dir = 1;
9430fb3f58eSDavid du Colombier 	}
9440fb3f58eSDavid du Colombier 	switch (hp->linkflag) {
94540ef9009SDavid du Colombier 	case LF_LINK:
94640ef9009SDavid du Colombier 	case LF_SYMLINK1:
94740ef9009SDavid du Colombier 	case LF_SYMLINK2:
94840ef9009SDavid du Colombier 	case LF_FIFO:
9490fb3f58eSDavid du Colombier 		blksleft = 0;
9503e12c5d1SDavid du Colombier 		break;
9513e12c5d1SDavid du Colombier 	}
95223605fa3SDavid du Colombier 	if (relative) {
95323605fa3SDavid du Colombier 		if(fname[0] == '/')
95423605fa3SDavid du Colombier 			*--fname = '.';
95523605fa3SDavid du Colombier 		else if(fname[0] == '#'){
95623605fa3SDavid du Colombier 			*--fname = '/';
95723605fa3SDavid du Colombier 			*--fname = '.';
95823605fa3SDavid du Colombier 		}
95923605fa3SDavid du Colombier 	}
9600fb3f58eSDavid du Colombier 	if (verb == Xtract) {
9610fb3f58eSDavid du Colombier 		cleanname(fname);
9620fb3f58eSDavid du Colombier 		switch (hp->linkflag) {
9630fb3f58eSDavid du Colombier 		case LF_LINK:
9640fb3f58eSDavid du Colombier 		case LF_SYMLINK1:
9650fb3f58eSDavid du Colombier 		case LF_SYMLINK2:
9660fb3f58eSDavid du Colombier 			fprint(2, "%s: can't make (sym)link %s\n",
9670fb3f58eSDavid du Colombier 				argv0, fname);
9683e12c5d1SDavid du Colombier 			break;
9690fb3f58eSDavid du Colombier 		case LF_FIFO:
9700fb3f58eSDavid du Colombier 			fprint(2, "%s: can't make fifo %s\n", argv0, fname);
9710fb3f58eSDavid du Colombier 			break;
9720fb3f58eSDavid du Colombier 		default:
9730fb3f58eSDavid du Colombier 			if (!keepexisting || access(fname, AEXIST) < 0) {
9740fb3f58eSDavid du Colombier 				int rw = (dir? OREAD: OWRITE);
9753e12c5d1SDavid du Colombier 
9760fb3f58eSDavid du Colombier 				fd = create(fname, rw, mode);
9770fb3f58eSDavid du Colombier 				if (fd < 0) {
9780fb3f58eSDavid du Colombier 					mkpdirs(fname);
9790fb3f58eSDavid du Colombier 					fd = create(fname, rw, mode);
9803e12c5d1SDavid du Colombier 				}
9810fb3f58eSDavid du Colombier 				if (fd < 0 &&
982312a1df1SDavid du Colombier 				    (!dir || xaccess(fname, AEXIST) < 0))
9838b6d9ba0SDavid du Colombier 				    	cantcreate(fname, mode);
9843e12c5d1SDavid du Colombier 			}
9850fb3f58eSDavid du Colombier 			if (fd >= 0 && verbose)
9860fb3f58eSDavid du Colombier 				fprint(2, "%s\n", fname);
9870fb3f58eSDavid du Colombier 			break;
9880fb3f58eSDavid du Colombier 		}
9890fb3f58eSDavid du Colombier 	} else if (verbose) {
9900fb3f58eSDavid du Colombier 		char *cp = ctime(mtime);
9913e12c5d1SDavid du Colombier 
9920fb3f58eSDavid du Colombier 		print("%M %8lld %-12.12s %-4.4s %s\n",
9930fb3f58eSDavid du Colombier 			mode, bytes, cp+4, cp+24, fname);
9947dd7cddfSDavid du Colombier 	} else
9950fb3f58eSDavid du Colombier 		print("%s\n", fname);
9963e12c5d1SDavid du Colombier 
9975437ee90SDavid du Colombier 	if (blksleft == 0)
9985437ee90SDavid du Colombier 		bytes = 0;
9990fb3f58eSDavid du Colombier 	for (; blksleft > 0; blksleft -= blksread) {
10004f281771SDavid du Colombier 		hbp = getblkrd(ar, (fd >= 0? Alldata: Justnxthdr));
10010fb3f58eSDavid du Colombier 		if (hbp == nil)
10020fb3f58eSDavid du Colombier 			sysfatal("unexpected EOF on archive extracting %s",
10030fb3f58eSDavid du Colombier 				fname);
10040fb3f58eSDavid du Colombier 		blksread = gothowmany(blksleft);
10055437ee90SDavid du Colombier 		if (blksread <= 0)
10065437ee90SDavid du Colombier 			fprint(2, "%s: got %ld blocks reading %s!\n",
10075437ee90SDavid du Colombier 				argv0, blksread, fname);
10080fb3f58eSDavid du Colombier 		wrbytes = Tblock*blksread;
10090fb3f58eSDavid du Colombier 		if(wrbytes > bytes)
10100fb3f58eSDavid du Colombier 			wrbytes = bytes;
10110fb3f58eSDavid du Colombier 		if (fd >= 0 && write(fd, hbp->data, wrbytes) != wrbytes)
10120fb3f58eSDavid du Colombier 			sysfatal("write error on %s: %r", fname);
10130fb3f58eSDavid du Colombier 		putreadblks(ar, blksread);
10140fb3f58eSDavid du Colombier 		bytes -= wrbytes;
101540ef9009SDavid du Colombier 	}
10165437ee90SDavid du Colombier 	if (bytes > 0)
10175437ee90SDavid du Colombier 		fprint(2,
10185437ee90SDavid du Colombier 		    "%s: %lld bytes uncopied at eof; %s not fully extracted\n",
10195437ee90SDavid du Colombier 			argv0, bytes, fname);
10200fb3f58eSDavid du Colombier 	if (fd >= 0) {
10213e12c5d1SDavid du Colombier 		/*
10220fb3f58eSDavid du Colombier 		 * directories should be wstated after we're done
10230fb3f58eSDavid du Colombier 		 * creating files in them.
10243e12c5d1SDavid du Colombier 		 */
10250fb3f58eSDavid du Colombier 		if (settime) {
10260fb3f58eSDavid du Colombier 			Dir nd;
10270fb3f58eSDavid du Colombier 
10280fb3f58eSDavid du Colombier 			nulldir(&nd);
10290fb3f58eSDavid du Colombier 			nd.mtime = mtime;
1030*499069deSDavid du Colombier 			dirfwstat(fd, &nd);
1031*499069deSDavid du Colombier 			if (isustar(hp)) {
1032*499069deSDavid du Colombier 				nulldir(&nd);
10330fb3f58eSDavid du Colombier 				nd.gid = hp->gname;
10340fb3f58eSDavid du Colombier 				dirfwstat(fd, &nd);
10350fb3f58eSDavid du Colombier 			}
1036*499069deSDavid du Colombier 		}
10370fb3f58eSDavid du Colombier 		close(fd);
10380fb3f58eSDavid du Colombier 	}
10390fb3f58eSDavid du Colombier }
10400fb3f58eSDavid du Colombier 
10410fb3f58eSDavid du Colombier static void
10420fb3f58eSDavid du Colombier skip(int ar, Hdr *hp, char *fname)
10433e12c5d1SDavid du Colombier {
10440fb3f58eSDavid du Colombier 	ulong blksleft, blksread;
10450fb3f58eSDavid du Colombier 	Hdr *hbp;
10460fb3f58eSDavid du Colombier 
1047bbd061d4SDavid du Colombier 	for (blksleft = BYTES2TBLKS(arsize(hp)); blksleft > 0;
10484f281771SDavid du Colombier 	     blksleft -= blksread) {
10494f281771SDavid du Colombier 		hbp = getblkrd(ar, Justnxthdr);
10500fb3f58eSDavid du Colombier 		if (hbp == nil)
10510fb3f58eSDavid du Colombier 			sysfatal("unexpected EOF on archive extracting %s",
10520fb3f58eSDavid du Colombier 				fname);
10530fb3f58eSDavid du Colombier 		blksread = gothowmany(blksleft);
10540fb3f58eSDavid du Colombier 		putreadblks(ar, blksread);
10550fb3f58eSDavid du Colombier 	}
10560fb3f58eSDavid du Colombier }
10570fb3f58eSDavid du Colombier 
10580fb3f58eSDavid du Colombier static char *
10590fb3f58eSDavid du Colombier extract(char **argv)
10600fb3f58eSDavid du Colombier {
10610fb3f58eSDavid du Colombier 	int ar;
10620fb3f58eSDavid du Colombier 	char *longname;
10630fb3f58eSDavid du Colombier 	Hdr *hp;
10640fb3f58eSDavid du Colombier 	Compress *comp = nil;
10650fb3f58eSDavid du Colombier 	Pushstate ps;
10660fb3f58eSDavid du Colombier 
10670fb3f58eSDavid du Colombier 	if (usefile) {
10680fb3f58eSDavid du Colombier 		ar = open(usefile, OREAD);
10690fb3f58eSDavid du Colombier 		comp = compmethod(usefile);
10700fb3f58eSDavid du Colombier 	} else
10710fb3f58eSDavid du Colombier 		ar = Stdin;
10720fb3f58eSDavid du Colombier 	if (comp)
10730fb3f58eSDavid du Colombier 		ar = push(ar, comp->decomp, Input, &ps);
10740fb3f58eSDavid du Colombier 	if (ar < 0)
10750fb3f58eSDavid du Colombier 		sysfatal("can't open archive %s: %r", usefile);
10760fb3f58eSDavid du Colombier 
10770fb3f58eSDavid du Colombier 	while ((hp = readhdr(ar)) != nil) {
10780fb3f58eSDavid du Colombier 		longname = name(hp);
10790fb3f58eSDavid du Colombier 		if (match(longname, argv))
10800fb3f58eSDavid du Colombier 			extract1(ar, hp, longname);
10810fb3f58eSDavid du Colombier 		else
10820fb3f58eSDavid du Colombier 			skip(ar, hp, longname);
10830fb3f58eSDavid du Colombier 	}
10840fb3f58eSDavid du Colombier 
10850fb3f58eSDavid du Colombier 	if (comp)
10860fb3f58eSDavid du Colombier 		return pushclose(&ps);
10870fb3f58eSDavid du Colombier 	if (ar > Stderr)
10880fb3f58eSDavid du Colombier 		close(ar);
10890fb3f58eSDavid du Colombier 	return nil;
10903e12c5d1SDavid du Colombier }
10913e12c5d1SDavid du Colombier 
10923e12c5d1SDavid du Colombier void
10930fb3f58eSDavid du Colombier main(int argc, char *argv[])
10943e12c5d1SDavid du Colombier {
10950fb3f58eSDavid du Colombier 	int errflg = 0;
10960fb3f58eSDavid du Colombier 	char *ret = nil;
10970fb3f58eSDavid du Colombier 
10980fb3f58eSDavid du Colombier 	fmtinstall('M', dirmodefmt);
10990fb3f58eSDavid du Colombier 
11000fb3f58eSDavid du Colombier 	TARGBEGIN {
11010fb3f58eSDavid du Colombier 	case 'c':
11020fb3f58eSDavid du Colombier 		docreate++;
11030fb3f58eSDavid du Colombier 		verb = Replace;
11040fb3f58eSDavid du Colombier 		break;
11050fb3f58eSDavid du Colombier 	case 'f':
11060fb3f58eSDavid du Colombier 		usefile = EARGF(usage());
11070fb3f58eSDavid du Colombier 		break;
11080fb3f58eSDavid du Colombier 	case 'g':
11090fb3f58eSDavid du Colombier 		argid = strtoul(EARGF(usage()), 0, 0);
11100fb3f58eSDavid du Colombier 		break;
11110fb3f58eSDavid du Colombier 	case 'k':
11120fb3f58eSDavid du Colombier 		keepexisting++;
11130fb3f58eSDavid du Colombier 		break;
11140fb3f58eSDavid du Colombier 	case 'm':	/* compatibility */
11150fb3f58eSDavid du Colombier 		settime = 0;
11160fb3f58eSDavid du Colombier 		break;
11170fb3f58eSDavid du Colombier 	case 'p':
11180fb3f58eSDavid du Colombier 		posix++;
11190fb3f58eSDavid du Colombier 		break;
11200fb3f58eSDavid du Colombier 	case 'P':
11210fb3f58eSDavid du Colombier 		posix = 0;
11220fb3f58eSDavid du Colombier 		break;
11230fb3f58eSDavid du Colombier 	case 'r':
11240fb3f58eSDavid du Colombier 		verb = Replace;
11250fb3f58eSDavid du Colombier 		break;
11260fb3f58eSDavid du Colombier 	case 'R':
112723605fa3SDavid du Colombier 		relative = 0;
11280fb3f58eSDavid du Colombier 		break;
11290fb3f58eSDavid du Colombier 	case 't':
11300fb3f58eSDavid du Colombier 		verb = Toc;
11310fb3f58eSDavid du Colombier 		break;
11320fb3f58eSDavid du Colombier 	case 'T':
11330fb3f58eSDavid du Colombier 		settime++;
11340fb3f58eSDavid du Colombier 		break;
11350fb3f58eSDavid du Colombier 	case 'u':
11360fb3f58eSDavid du Colombier 		aruid = strtoul(EARGF(usage()), 0, 0);
11370fb3f58eSDavid du Colombier 		break;
11380fb3f58eSDavid du Colombier 	case 'v':
11390fb3f58eSDavid du Colombier 		verbose++;
11400fb3f58eSDavid du Colombier 		break;
11410fb3f58eSDavid du Colombier 	case 'x':
11420fb3f58eSDavid du Colombier 		verb = Xtract;
11430fb3f58eSDavid du Colombier 		break;
11440fb3f58eSDavid du Colombier 	case 'z':
11450fb3f58eSDavid du Colombier 		docompress++;
11460fb3f58eSDavid du Colombier 		break;
11470fb3f58eSDavid du Colombier 	case '-':
11480fb3f58eSDavid du Colombier 		break;
11490fb3f58eSDavid du Colombier 	default:
11500fb3f58eSDavid du Colombier 		fprint(2, "tar: unknown letter %C\n", TARGC());
11510fb3f58eSDavid du Colombier 		errflg++;
11520fb3f58eSDavid du Colombier 		break;
11530fb3f58eSDavid du Colombier 	} TARGEND
11540fb3f58eSDavid du Colombier 
11550fb3f58eSDavid du Colombier 	if (argc < 0 || errflg)
11560fb3f58eSDavid du Colombier 		usage();
11570fb3f58eSDavid du Colombier 
11580fb3f58eSDavid du Colombier 	initblks();
11590fb3f58eSDavid du Colombier 	switch (verb) {
11600fb3f58eSDavid du Colombier 	case Toc:
11610fb3f58eSDavid du Colombier 	case Xtract:
11620fb3f58eSDavid du Colombier 		ret = extract(argv);
11630fb3f58eSDavid du Colombier 		break;
11640fb3f58eSDavid du Colombier 	case Replace:
11650fb3f58eSDavid du Colombier 		if (getwd(origdir, sizeof origdir) == nil)
11660fb3f58eSDavid du Colombier 			strcpy(origdir, "/tmp");
11670fb3f58eSDavid du Colombier 		ret = replace(argv);
11680fb3f58eSDavid du Colombier 		break;
11690fb3f58eSDavid du Colombier 	default:
11700fb3f58eSDavid du Colombier 		usage();
11710fb3f58eSDavid du Colombier 		break;
11720fb3f58eSDavid du Colombier 	}
11730fb3f58eSDavid du Colombier 	exits(ret);
11743e12c5d1SDavid du Colombier }
1175