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