17dd7cddfSDavid du Colombier #include <u.h> 27dd7cddfSDavid du Colombier #include <libc.h> 37dd7cddfSDavid du Colombier #include <bio.h> 480ee5cbfSDavid du Colombier #include <flate.h> 57dd7cddfSDavid du Colombier #include "zip.h" 67dd7cddfSDavid du Colombier 77dd7cddfSDavid du Colombier enum 87dd7cddfSDavid du Colombier { 97dd7cddfSDavid du Colombier HeadAlloc = 64, 107dd7cddfSDavid du Colombier }; 117dd7cddfSDavid du Colombier 127dd7cddfSDavid du Colombier static void zip(Biobuf *bout, char *file, int stdout); 137dd7cddfSDavid du Colombier static void zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout); 147dd7cddfSDavid du Colombier static int crcread(void *fd, void *buf, int n); 157dd7cddfSDavid du Colombier static int zwrite(void *bout, void *buf, int n); 167dd7cddfSDavid du Colombier static void put4(Biobuf *b, ulong v); 177dd7cddfSDavid du Colombier static void put2(Biobuf *b, int v); 187dd7cddfSDavid du Colombier static void put1(Biobuf *b, int v); 197dd7cddfSDavid du Colombier static void header(Biobuf *bout, ZipHead *zh); 207dd7cddfSDavid du Colombier static void trailer(Biobuf *bout, ZipHead *zh, vlong off); 217dd7cddfSDavid du Colombier static void putCDir(Biobuf *bout); 227dd7cddfSDavid du Colombier 237dd7cddfSDavid du Colombier static void error(char*, ...); 247dd7cddfSDavid du Colombier #pragma varargck argpos error 1 257dd7cddfSDavid du Colombier 267dd7cddfSDavid du Colombier static Biobuf bout; 277dd7cddfSDavid du Colombier static ulong crc; 2880ee5cbfSDavid du Colombier static ulong *crctab; 297dd7cddfSDavid du Colombier static int debug; 307dd7cddfSDavid du Colombier static int eof; 317dd7cddfSDavid du Colombier static int level; 327dd7cddfSDavid du Colombier static int nzheads; 337dd7cddfSDavid du Colombier static ulong totr; 347dd7cddfSDavid du Colombier static ulong totw; 357dd7cddfSDavid du Colombier static int verbose; 367dd7cddfSDavid du Colombier static int zhalloc; 377dd7cddfSDavid du Colombier static ZipHead *zheads; 387dd7cddfSDavid du Colombier static jmp_buf zjmp; 397dd7cddfSDavid du Colombier 407dd7cddfSDavid du Colombier void 417dd7cddfSDavid du Colombier usage(void) 427dd7cddfSDavid du Colombier { 437dd7cddfSDavid du Colombier fprint(2, "usage: zip [-vD] [-1-9] [-f zipfile] file ...\n"); 447dd7cddfSDavid du Colombier exits("usage"); 457dd7cddfSDavid du Colombier } 467dd7cddfSDavid du Colombier 477dd7cddfSDavid du Colombier void 487dd7cddfSDavid du Colombier main(int argc, char *argv[]) 497dd7cddfSDavid du Colombier { 507dd7cddfSDavid du Colombier char *zfile; 5180ee5cbfSDavid du Colombier int i, fd, err; 527dd7cddfSDavid du Colombier 537dd7cddfSDavid du Colombier zfile = nil; 547dd7cddfSDavid du Colombier level = 6; 557dd7cddfSDavid du Colombier ARGBEGIN{ 567dd7cddfSDavid du Colombier case 'D': 577dd7cddfSDavid du Colombier debug++; 587dd7cddfSDavid du Colombier break; 597dd7cddfSDavid du Colombier case 'f': 607dd7cddfSDavid du Colombier zfile = ARGF(); 617dd7cddfSDavid du Colombier if(zfile == nil) 627dd7cddfSDavid du Colombier usage(); 637dd7cddfSDavid du Colombier break; 647dd7cddfSDavid du Colombier case 'v': 657dd7cddfSDavid du Colombier verbose++; 667dd7cddfSDavid du Colombier break; 677dd7cddfSDavid du Colombier case '1': case '2': case '3': case '4': 687dd7cddfSDavid du Colombier case '5': case '6': case '7': case '8': case '9': 697dd7cddfSDavid du Colombier level = ARGC() - '0'; 707dd7cddfSDavid du Colombier break; 717dd7cddfSDavid du Colombier default: 727dd7cddfSDavid du Colombier usage(); 737dd7cddfSDavid du Colombier break; 747dd7cddfSDavid du Colombier }ARGEND 757dd7cddfSDavid du Colombier 767dd7cddfSDavid du Colombier if(argc == 0) 777dd7cddfSDavid du Colombier usage(); 787dd7cddfSDavid du Colombier 7980ee5cbfSDavid du Colombier crctab = mkcrctab(ZCrcPoly); 8080ee5cbfSDavid du Colombier err = deflateinit(); 8180ee5cbfSDavid du Colombier if(err != FlateOk) 8280ee5cbfSDavid du Colombier sysfatal("deflateinit failed: %s\n", flateerr(err)); 837dd7cddfSDavid du Colombier 847dd7cddfSDavid du Colombier if(zfile == nil) 857dd7cddfSDavid du Colombier fd = 1; 867dd7cddfSDavid du Colombier else{ 877dd7cddfSDavid du Colombier fd = create(zfile, OWRITE, 0664); 887dd7cddfSDavid du Colombier if(fd < 0) 897dd7cddfSDavid du Colombier sysfatal("can't create %s: %r\n", zfile); 907dd7cddfSDavid du Colombier } 917dd7cddfSDavid du Colombier Binit(&bout, fd, OWRITE); 927dd7cddfSDavid du Colombier 937dd7cddfSDavid du Colombier if(setjmp(zjmp)){ 947dd7cddfSDavid du Colombier if(zfile != nil){ 957dd7cddfSDavid du Colombier fprint(2, "zip: removing output file %s\n", zfile); 967dd7cddfSDavid du Colombier remove(zfile); 977dd7cddfSDavid du Colombier } 987dd7cddfSDavid du Colombier exits("errors"); 997dd7cddfSDavid du Colombier } 1007dd7cddfSDavid du Colombier 1017dd7cddfSDavid du Colombier for(i = 0; i < argc; i++) 1027dd7cddfSDavid du Colombier zip(&bout, argv[i], zfile == nil); 1037dd7cddfSDavid du Colombier 1047dd7cddfSDavid du Colombier putCDir(&bout); 1057dd7cddfSDavid du Colombier 1067dd7cddfSDavid du Colombier exits(nil); 1077dd7cddfSDavid du Colombier } 1087dd7cddfSDavid du Colombier 1097dd7cddfSDavid du Colombier static void 1107dd7cddfSDavid du Colombier zip(Biobuf *bout, char *file, int stdout) 1117dd7cddfSDavid du Colombier { 1127dd7cddfSDavid du Colombier Tm *t; 1137dd7cddfSDavid du Colombier ZipHead *zh; 1149a747e4fSDavid du Colombier Dir *dir; 1157dd7cddfSDavid du Colombier vlong off; 11680ee5cbfSDavid du Colombier int fd, err; 1177dd7cddfSDavid du Colombier 1187dd7cddfSDavid du Colombier fd = open(file, OREAD); 1197dd7cddfSDavid du Colombier if(fd < 0) 1207dd7cddfSDavid du Colombier error("can't open %s: %r", file); 1219a747e4fSDavid du Colombier dir = dirfstat(fd); 1229a747e4fSDavid du Colombier if(dir == nil) 1237dd7cddfSDavid du Colombier error("can't stat %s: %r", file); 1247dd7cddfSDavid du Colombier 1257dd7cddfSDavid du Colombier /* 1267dd7cddfSDavid du Colombier * create the header 1277dd7cddfSDavid du Colombier */ 1287dd7cddfSDavid du Colombier if(nzheads >= zhalloc){ 1297dd7cddfSDavid du Colombier zhalloc += HeadAlloc; 1307dd7cddfSDavid du Colombier zheads = realloc(zheads, zhalloc * sizeof(ZipHead)); 1317dd7cddfSDavid du Colombier if(zheads == nil) 1327dd7cddfSDavid du Colombier error("out of memory"); 1337dd7cddfSDavid du Colombier } 1347dd7cddfSDavid du Colombier zh = &zheads[nzheads++]; 1357dd7cddfSDavid du Colombier zh->madeos = ZDos; 1367dd7cddfSDavid du Colombier zh->madevers = (2 * 10) + 0; 1377dd7cddfSDavid du Colombier zh->extos = ZDos; 1387dd7cddfSDavid du Colombier zh->extvers = (2 * 10) + 0; 1397dd7cddfSDavid du Colombier 1409a747e4fSDavid du Colombier t = localtime(dir->mtime); 1417dd7cddfSDavid du Colombier zh->modtime = (t->hour<<11) | (t->min<<5) | (t->sec>>1); 1427dd7cddfSDavid du Colombier zh->moddate = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; 1437dd7cddfSDavid du Colombier 1447dd7cddfSDavid du Colombier zh->flags = 0; 1457dd7cddfSDavid du Colombier zh->crc = 0; 1467dd7cddfSDavid du Colombier zh->csize = 0; 1477dd7cddfSDavid du Colombier zh->uncsize = 0; 1487dd7cddfSDavid du Colombier zh->file = strdup(file); 1497dd7cddfSDavid du Colombier if(zh->file == nil) 1507dd7cddfSDavid du Colombier error("out of memory"); 1517dd7cddfSDavid du Colombier zh->iattr = 0; 1527dd7cddfSDavid du Colombier zh->eattr = ZDArch; 1539a747e4fSDavid du Colombier if((dir->mode & 0700) == 0) 1547dd7cddfSDavid du Colombier zh->eattr |= ZDROnly; 1557dd7cddfSDavid du Colombier zh->off = Boffset(bout); 1567dd7cddfSDavid du Colombier 1579a747e4fSDavid du Colombier if(dir->mode & DMDIR){ 1587dd7cddfSDavid du Colombier zh->eattr |= ZDDir; 1597dd7cddfSDavid du Colombier zh->meth = 0; 1607dd7cddfSDavid du Colombier zipDir(bout, fd, zh, stdout); 1617dd7cddfSDavid du Colombier }else{ 1627dd7cddfSDavid du Colombier zh->meth = 8; 1637dd7cddfSDavid du Colombier if(stdout) 1647dd7cddfSDavid du Colombier zh->flags |= ZTrailInfo; 1657dd7cddfSDavid du Colombier off = Boffset(bout); 1667dd7cddfSDavid du Colombier header(bout, zh); 1677dd7cddfSDavid du Colombier 1687dd7cddfSDavid du Colombier crc = 0; 1697dd7cddfSDavid du Colombier eof = 0; 1707dd7cddfSDavid du Colombier totr = 0; 1717dd7cddfSDavid du Colombier totw = 0; 17280ee5cbfSDavid du Colombier err = deflate(bout, zwrite, (void*)fd, crcread, level, debug); 17380ee5cbfSDavid du Colombier if(err != FlateOk) 17480ee5cbfSDavid du Colombier error("deflate failed: %s: %r", flateerr(err)); 1757dd7cddfSDavid du Colombier 1767dd7cddfSDavid du Colombier zh->csize = totw; 1777dd7cddfSDavid du Colombier zh->uncsize = totr; 1787dd7cddfSDavid du Colombier zh->crc = crc; 1797dd7cddfSDavid du Colombier trailer(bout, zh, off); 1807dd7cddfSDavid du Colombier } 1817dd7cddfSDavid du Colombier close(fd); 1829a747e4fSDavid du Colombier free(dir); 1837dd7cddfSDavid du Colombier } 1847dd7cddfSDavid du Colombier 1857dd7cddfSDavid du Colombier static void 1867dd7cddfSDavid du Colombier zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout) 1877dd7cddfSDavid du Colombier { 1889a747e4fSDavid du Colombier Dir *dirs; 1897dd7cddfSDavid du Colombier char *file, *pfile; 1907dd7cddfSDavid du Colombier int i, nf, nd; 1917dd7cddfSDavid du Colombier 1927dd7cddfSDavid du Colombier nf = strlen(zh->file) + 1; 1937dd7cddfSDavid du Colombier if(strcmp(zh->file, ".") == 0){ 1947dd7cddfSDavid du Colombier nzheads--; 1957dd7cddfSDavid du Colombier free(zh->file); 1967dd7cddfSDavid du Colombier pfile = ""; 1977dd7cddfSDavid du Colombier nf = 1; 1987dd7cddfSDavid du Colombier }else{ 1997dd7cddfSDavid du Colombier nf++; 2007dd7cddfSDavid du Colombier pfile = malloc(nf); 2017dd7cddfSDavid du Colombier if(pfile == nil) 2027dd7cddfSDavid du Colombier error("out of memory"); 2037dd7cddfSDavid du Colombier snprint(pfile, nf, "%s/", zh->file); 2047dd7cddfSDavid du Colombier free(zh->file); 2057dd7cddfSDavid du Colombier zh->file = pfile; 2067dd7cddfSDavid du Colombier header(bout, zh); 2077dd7cddfSDavid du Colombier } 2087dd7cddfSDavid du Colombier 2099a747e4fSDavid du Colombier nf += 256; /* plenty of room */ 2107dd7cddfSDavid du Colombier file = malloc(nf); 2117dd7cddfSDavid du Colombier if(file == nil) 2127dd7cddfSDavid du Colombier error("out of memory"); 213*d9306527SDavid du Colombier while((nd = dirread(fd, &dirs)) > 0){ 2147dd7cddfSDavid du Colombier for(i = 0; i < nd; i++){ 2157dd7cddfSDavid du Colombier snprint(file, nf, "%s%s", pfile, dirs[i].name); 2167dd7cddfSDavid du Colombier zip(bout, file, stdout); 2177dd7cddfSDavid du Colombier } 2189a747e4fSDavid du Colombier free(dirs); 2197dd7cddfSDavid du Colombier } 2207dd7cddfSDavid du Colombier } 2217dd7cddfSDavid du Colombier 2227dd7cddfSDavid du Colombier static void 2237dd7cddfSDavid du Colombier header(Biobuf *bout, ZipHead *zh) 2247dd7cddfSDavid du Colombier { 2257dd7cddfSDavid du Colombier int flen; 2267dd7cddfSDavid du Colombier 2277dd7cddfSDavid du Colombier if(verbose) 2287dd7cddfSDavid du Colombier fprint(2, "adding %s\n", zh->file); 2297dd7cddfSDavid du Colombier put4(bout, ZHeader); 2307dd7cddfSDavid du Colombier put1(bout, zh->extvers); 2317dd7cddfSDavid du Colombier put1(bout, zh->extos); 2327dd7cddfSDavid du Colombier put2(bout, zh->flags); 2337dd7cddfSDavid du Colombier put2(bout, zh->meth); 2347dd7cddfSDavid du Colombier put2(bout, zh->modtime); 2357dd7cddfSDavid du Colombier put2(bout, zh->moddate); 2367dd7cddfSDavid du Colombier put4(bout, zh->crc); 2377dd7cddfSDavid du Colombier put4(bout, zh->csize); 2387dd7cddfSDavid du Colombier put4(bout, zh->uncsize); 2397dd7cddfSDavid du Colombier flen = strlen(zh->file); 2407dd7cddfSDavid du Colombier put2(bout, flen); 2417dd7cddfSDavid du Colombier put2(bout, 0); 2427dd7cddfSDavid du Colombier if(Bwrite(bout, zh->file, flen) != flen) 2437dd7cddfSDavid du Colombier error("write error"); 2447dd7cddfSDavid du Colombier } 2457dd7cddfSDavid du Colombier 2467dd7cddfSDavid du Colombier static void 2477dd7cddfSDavid du Colombier trailer(Biobuf *bout, ZipHead *zh, vlong off) 2487dd7cddfSDavid du Colombier { 2497dd7cddfSDavid du Colombier vlong coff; 2507dd7cddfSDavid du Colombier 2517dd7cddfSDavid du Colombier coff = -1; 2527dd7cddfSDavid du Colombier if(!(zh->flags & ZTrailInfo)){ 2537dd7cddfSDavid du Colombier coff = Boffset(bout); 2547dd7cddfSDavid du Colombier if(Bseek(bout, off + ZHeadCrc, 0) < 0) 2557dd7cddfSDavid du Colombier error("can't seek in archive"); 2567dd7cddfSDavid du Colombier } 2577dd7cddfSDavid du Colombier put4(bout, zh->crc); 2587dd7cddfSDavid du Colombier put4(bout, zh->csize); 2597dd7cddfSDavid du Colombier put4(bout, zh->uncsize); 2607dd7cddfSDavid du Colombier if(!(zh->flags & ZTrailInfo)){ 2617dd7cddfSDavid du Colombier if(Bseek(bout, coff, 0) < 0) 2627dd7cddfSDavid du Colombier error("can't seek in archive"); 2637dd7cddfSDavid du Colombier } 2647dd7cddfSDavid du Colombier } 2657dd7cddfSDavid du Colombier 2667dd7cddfSDavid du Colombier static void 2677dd7cddfSDavid du Colombier cheader(Biobuf *bout, ZipHead *zh) 2687dd7cddfSDavid du Colombier { 2697dd7cddfSDavid du Colombier int flen; 2707dd7cddfSDavid du Colombier 2717dd7cddfSDavid du Colombier put4(bout, ZCHeader); 2727dd7cddfSDavid du Colombier put1(bout, zh->madevers); 2737dd7cddfSDavid du Colombier put1(bout, zh->madeos); 2747dd7cddfSDavid du Colombier put1(bout, zh->extvers); 2757dd7cddfSDavid du Colombier put1(bout, zh->extos); 2767dd7cddfSDavid du Colombier put2(bout, zh->flags & ~ZTrailInfo); 2777dd7cddfSDavid du Colombier put2(bout, zh->meth); 2787dd7cddfSDavid du Colombier put2(bout, zh->modtime); 2797dd7cddfSDavid du Colombier put2(bout, zh->moddate); 2807dd7cddfSDavid du Colombier put4(bout, zh->crc); 2817dd7cddfSDavid du Colombier put4(bout, zh->csize); 2827dd7cddfSDavid du Colombier put4(bout, zh->uncsize); 2837dd7cddfSDavid du Colombier flen = strlen(zh->file); 2847dd7cddfSDavid du Colombier put2(bout, flen); 2857dd7cddfSDavid du Colombier put2(bout, 0); 2867dd7cddfSDavid du Colombier put2(bout, 0); 2877dd7cddfSDavid du Colombier put2(bout, 0); 2887dd7cddfSDavid du Colombier put2(bout, zh->iattr); 2897dd7cddfSDavid du Colombier put4(bout, zh->eattr); 2907dd7cddfSDavid du Colombier put4(bout, zh->off); 2917dd7cddfSDavid du Colombier if(Bwrite(bout, zh->file, flen) != flen) 2927dd7cddfSDavid du Colombier error("write error"); 2937dd7cddfSDavid du Colombier } 2947dd7cddfSDavid du Colombier 2957dd7cddfSDavid du Colombier static void 2967dd7cddfSDavid du Colombier putCDir(Biobuf *bout) 2977dd7cddfSDavid du Colombier { 2987dd7cddfSDavid du Colombier vlong hoff, ecoff; 2997dd7cddfSDavid du Colombier int i; 3007dd7cddfSDavid du Colombier 3017dd7cddfSDavid du Colombier hoff = Boffset(bout); 3027dd7cddfSDavid du Colombier 3037dd7cddfSDavid du Colombier for(i = 0; i < nzheads; i++) 3047dd7cddfSDavid du Colombier cheader(bout, &zheads[i]); 3057dd7cddfSDavid du Colombier 3067dd7cddfSDavid du Colombier ecoff = Boffset(bout); 3077dd7cddfSDavid du Colombier 3087dd7cddfSDavid du Colombier if(nzheads >= (1 << 16)) 3097dd7cddfSDavid du Colombier error("too many entries in zip file: max %d", (1 << 16) - 1); 3107dd7cddfSDavid du Colombier put4(bout, ZECHeader); 3117dd7cddfSDavid du Colombier put2(bout, 0); 3127dd7cddfSDavid du Colombier put2(bout, 0); 3137dd7cddfSDavid du Colombier put2(bout, nzheads); 3147dd7cddfSDavid du Colombier put2(bout, nzheads); 3157dd7cddfSDavid du Colombier put4(bout, ecoff - hoff); 3167dd7cddfSDavid du Colombier put4(bout, hoff); 3177dd7cddfSDavid du Colombier put2(bout, 0); 3187dd7cddfSDavid du Colombier } 3197dd7cddfSDavid du Colombier 3207dd7cddfSDavid du Colombier static int 3217dd7cddfSDavid du Colombier crcread(void *fd, void *buf, int n) 3227dd7cddfSDavid du Colombier { 3237dd7cddfSDavid du Colombier int nr, m; 3247dd7cddfSDavid du Colombier 3257dd7cddfSDavid du Colombier nr = 0; 3267dd7cddfSDavid du Colombier for(; !eof && n > 0; n -= m){ 3277dd7cddfSDavid du Colombier m = read((int)fd, (char*)buf+nr, n); 3287dd7cddfSDavid du Colombier if(m <= 0){ 3297dd7cddfSDavid du Colombier eof = 1; 33080ee5cbfSDavid du Colombier if(m < 0) 33180ee5cbfSDavid du Colombier { 33280ee5cbfSDavid du Colombier fprint(2, "input error %r\n"); 33380ee5cbfSDavid du Colombier return -1; 33480ee5cbfSDavid du Colombier } 3357dd7cddfSDavid du Colombier break; 3367dd7cddfSDavid du Colombier } 3377dd7cddfSDavid du Colombier nr += m; 3387dd7cddfSDavid du Colombier } 33980ee5cbfSDavid du Colombier crc = blockcrc(crctab, crc, buf, nr); 3407dd7cddfSDavid du Colombier totr += nr; 3417dd7cddfSDavid du Colombier return nr; 3427dd7cddfSDavid du Colombier } 3437dd7cddfSDavid du Colombier 3447dd7cddfSDavid du Colombier static int 3457dd7cddfSDavid du Colombier zwrite(void *bout, void *buf, int n) 3467dd7cddfSDavid du Colombier { 3477dd7cddfSDavid du Colombier if(n != Bwrite(bout, buf, n)){ 3487dd7cddfSDavid du Colombier eof = 1; 3497dd7cddfSDavid du Colombier return -1; 3507dd7cddfSDavid du Colombier } 3517dd7cddfSDavid du Colombier totw += n; 3527dd7cddfSDavid du Colombier return n; 3537dd7cddfSDavid du Colombier } 3547dd7cddfSDavid du Colombier 3557dd7cddfSDavid du Colombier static void 3567dd7cddfSDavid du Colombier put4(Biobuf *b, ulong v) 3577dd7cddfSDavid du Colombier { 3587dd7cddfSDavid du Colombier int i; 3597dd7cddfSDavid du Colombier 3607dd7cddfSDavid du Colombier for(i = 0; i < 4; i++){ 3617dd7cddfSDavid du Colombier if(Bputc(b, v) < 0) 3627dd7cddfSDavid du Colombier error("write error"); 3637dd7cddfSDavid du Colombier v >>= 8; 3647dd7cddfSDavid du Colombier } 3657dd7cddfSDavid du Colombier } 3667dd7cddfSDavid du Colombier 3677dd7cddfSDavid du Colombier static void 3687dd7cddfSDavid du Colombier put2(Biobuf *b, int v) 3697dd7cddfSDavid du Colombier { 3707dd7cddfSDavid du Colombier int i; 3717dd7cddfSDavid du Colombier 3727dd7cddfSDavid du Colombier for(i = 0; i < 2; i++){ 3737dd7cddfSDavid du Colombier if(Bputc(b, v) < 0) 3747dd7cddfSDavid du Colombier error("write error"); 3757dd7cddfSDavid du Colombier v >>= 8; 3767dd7cddfSDavid du Colombier } 3777dd7cddfSDavid du Colombier } 3787dd7cddfSDavid du Colombier 3797dd7cddfSDavid du Colombier static void 3807dd7cddfSDavid du Colombier put1(Biobuf *b, int v) 3817dd7cddfSDavid du Colombier { 3827dd7cddfSDavid du Colombier if(Bputc(b, v)< 0) 3837dd7cddfSDavid du Colombier error("unexpected eof reading file information"); 3847dd7cddfSDavid du Colombier } 3857dd7cddfSDavid du Colombier 3867dd7cddfSDavid du Colombier static void 3877dd7cddfSDavid du Colombier error(char *fmt, ...) 3887dd7cddfSDavid du Colombier { 3897dd7cddfSDavid du Colombier va_list arg; 3907dd7cddfSDavid du Colombier 3919a747e4fSDavid du Colombier fprint(2, "zip: "); 3927dd7cddfSDavid du Colombier va_start(arg, fmt); 3939a747e4fSDavid du Colombier vfprint(2, fmt, arg); 3947dd7cddfSDavid du Colombier va_end(arg); 3959a747e4fSDavid du Colombier fprint(2, "\n"); 3967dd7cddfSDavid du Colombier 3977dd7cddfSDavid du Colombier longjmp(zjmp, 1); 3987dd7cddfSDavid du Colombier } 399