|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <flate.h> | 
|  | #include "zip.h" | 
|  |  | 
|  | enum | 
|  | { | 
|  | BufSize	= 4096 | 
|  | }; | 
|  |  | 
|  | static	int	cheader(Biobuf *bin, ZipHead *zh); | 
|  | static	int	copyout(int ofd, Biobuf *bin, long len); | 
|  | static	int	crcwrite(void *ofd, void *buf, int n); | 
|  | static	int	findCDir(Biobuf *bin, char *file); | 
|  | static	int	get1(Biobuf *b); | 
|  | static	int	get2(Biobuf *b); | 
|  | static	u32int	get4(Biobuf *b); | 
|  | static	char	*getname(Biobuf *b, int len); | 
|  | static	int	header(Biobuf *bin, ZipHead *zh); | 
|  | static	long	msdos2time(int time, int date); | 
|  | static	int	sunzip(Biobuf *bin); | 
|  | static	int	sunztable(Biobuf *bin); | 
|  | static	void	trailer(Biobuf *bin, ZipHead *zh); | 
|  | static	int	unzip(Biobuf *bin, char *file); | 
|  | static	int	unzipEntry(Biobuf *bin, ZipHead *czh); | 
|  | static	int	unztable(Biobuf *bin, char *file); | 
|  | static	int	wantFile(char *file); | 
|  |  | 
|  | static	void	*emalloc(u32int); | 
|  | static	void	error(char*, ...); | 
|  | /* #pragma	varargck	argpos	error	1 */ | 
|  |  | 
|  | static	Biobuf	bin; | 
|  | static	u32int	crc; | 
|  | static	u32int	*crctab; | 
|  | static	int	debug; | 
|  | static	char	*delfile; | 
|  | static	int	lower; | 
|  | static	int	nwant; | 
|  | static	u32int	rlen; | 
|  | static	int	settimes; | 
|  | static	int	stdout; | 
|  | static	int	verbose; | 
|  | static	char	**want; | 
|  | static	int	wbad; | 
|  | static	u32int	wlen; | 
|  | static	jmp_buf	zjmp; | 
|  |  | 
|  | static void | 
|  | usage(void) | 
|  | { | 
|  | fprint(2, "usage: unzip [-tsv] [-f zipfile] [file ...]\n"); | 
|  | exits("usage"); | 
|  | } | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | char *zfile; | 
|  | int fd, ok, table, stream; | 
|  |  | 
|  | table = 0; | 
|  | stream = 0; | 
|  | zfile = nil; | 
|  | ARGBEGIN{ | 
|  | case 'D': | 
|  | debug++; | 
|  | break; | 
|  | case 'c': | 
|  | stdout++; | 
|  | break; | 
|  | case 'i': | 
|  | lower++; | 
|  | break; | 
|  | case 'f': | 
|  | zfile = ARGF(); | 
|  | if(zfile == nil) | 
|  | usage(); | 
|  | break; | 
|  | case 's': | 
|  | stream++; | 
|  | break; | 
|  | case 't': | 
|  | table++; | 
|  | break; | 
|  | case 'T': | 
|  | settimes++; | 
|  | break; | 
|  | case 'v': | 
|  | verbose++; | 
|  | break; | 
|  | default: | 
|  | usage(); | 
|  | break; | 
|  | }ARGEND | 
|  |  | 
|  | nwant = argc; | 
|  | want = argv; | 
|  |  | 
|  | crctab = mkcrctab(ZCrcPoly); | 
|  | ok = inflateinit(); | 
|  | if(ok != FlateOk) | 
|  | sysfatal("inflateinit failed: %s\n", flateerr(ok)); | 
|  |  | 
|  | if(zfile == nil){ | 
|  | Binit(&bin, 0, OREAD); | 
|  | zfile = "<stdin>"; | 
|  | }else{ | 
|  | fd = open(zfile, OREAD); | 
|  | if(fd < 0) | 
|  | sysfatal("can't open %s: %r", zfile); | 
|  | Binit(&bin, fd, OREAD); | 
|  | } | 
|  |  | 
|  | if(table){ | 
|  | if(stream) | 
|  | ok = sunztable(&bin); | 
|  | else | 
|  | ok = unztable(&bin, zfile); | 
|  | }else{ | 
|  | if(stream) | 
|  | ok = sunzip(&bin); | 
|  | else | 
|  | ok = unzip(&bin, zfile); | 
|  | } | 
|  |  | 
|  | exits(ok ? nil: "errors"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * print the table of contents from the "central directory structure" | 
|  | */ | 
|  | static int | 
|  | unztable(Biobuf *bin, char *file) | 
|  | { | 
|  | ZipHead zh; | 
|  | int volatile entries; | 
|  |  | 
|  | entries = findCDir(bin, file); | 
|  | if(entries < 0) | 
|  | return 0; | 
|  |  | 
|  | if(verbose > 1) | 
|  | print("%d items in the archive\n", entries); | 
|  | while(entries-- > 0){ | 
|  | if(setjmp(zjmp)){ | 
|  | free(zh.file); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | memset(&zh, 0, sizeof(zh)); | 
|  | if(!cheader(bin, &zh)) | 
|  | return 1; | 
|  |  | 
|  | if(wantFile(zh.file)){ | 
|  | if(verbose) | 
|  | print("%-32s %10lud %s", zh.file, zh.uncsize, ctime(msdos2time(zh.modtime, zh.moddate))); | 
|  | else | 
|  | print("%s\n", zh.file); | 
|  |  | 
|  | if(verbose > 1){ | 
|  | print("\tmade by os %d vers %d.%d\n", zh.madeos, zh.madevers/10, zh.madevers % 10); | 
|  | print("\textract by os %d vers %d.%d\n", zh.extos, zh.extvers/10, zh.extvers % 10); | 
|  | print("\tflags %x\n", zh.flags); | 
|  | print("\tmethod %d\n", zh.meth); | 
|  | print("\tmod time %d\n", zh.modtime); | 
|  | print("\tmod date %d\n", zh.moddate); | 
|  | print("\tcrc %lux\n", zh.crc); | 
|  | print("\tcompressed size %lud\n", zh.csize); | 
|  | print("\tuncompressed size %lud\n", zh.uncsize); | 
|  | print("\tinternal attributes %ux\n", zh.iattr); | 
|  | print("\texternal attributes %lux\n", zh.eattr); | 
|  | print("\tstarts at %ld\n", zh.off); | 
|  | } | 
|  | } | 
|  |  | 
|  | free(zh.file); | 
|  | zh.file = nil; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * print the "local file header" table of contents | 
|  | */ | 
|  | static int | 
|  | sunztable(Biobuf *bin) | 
|  | { | 
|  | ZipHead zh; | 
|  | vlong off; | 
|  | u32int hcrc, hcsize, huncsize; | 
|  | int ok, err; | 
|  |  | 
|  | ok = 1; | 
|  | for(;;){ | 
|  | if(setjmp(zjmp)){ | 
|  | free(zh.file); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | memset(&zh, 0, sizeof(zh)); | 
|  | if(!header(bin, &zh)) | 
|  | return ok; | 
|  |  | 
|  | hcrc = zh.crc; | 
|  | hcsize = zh.csize; | 
|  | huncsize = zh.uncsize; | 
|  |  | 
|  | wlen = 0; | 
|  | rlen = 0; | 
|  | crc = 0; | 
|  | wbad = 0; | 
|  |  | 
|  | if(zh.meth == 0){ | 
|  | if(!copyout(-1, bin, zh.csize)) | 
|  | error("reading data for %s failed: %r", zh.file); | 
|  | }else if(zh.meth == 8){ | 
|  | off = Boffset(bin); | 
|  | err = inflate((void*)-1, crcwrite, bin, (int(*)(void*))Bgetc); | 
|  | if(err != FlateOk) | 
|  | error("inflate %s failed: %s", zh.file, flateerr(err)); | 
|  | rlen = Boffset(bin) - off; | 
|  | }else | 
|  | error("can't handle compression method %d for %s", zh.meth, zh.file); | 
|  |  | 
|  | trailer(bin, &zh); | 
|  |  | 
|  | if(wantFile(zh.file)){ | 
|  | if(verbose) | 
|  | print("%-32s %10lud %s", zh.file, zh.uncsize, ctime(msdos2time(zh.modtime, zh.moddate))); | 
|  | else | 
|  | print("%s\n", zh.file); | 
|  |  | 
|  | if(verbose > 1){ | 
|  | print("\textract by os %d vers %d.%d\n", zh.extos, zh.extvers / 10, zh.extvers % 10); | 
|  | print("\tflags %x\n", zh.flags); | 
|  | print("\tmethod %d\n", zh.meth); | 
|  | print("\tmod time %d\n", zh.modtime); | 
|  | print("\tmod date %d\n", zh.moddate); | 
|  | print("\tcrc %lux\n", zh.crc); | 
|  | print("\tcompressed size %lud\n", zh.csize); | 
|  | print("\tuncompressed size %lud\n", zh.uncsize); | 
|  | if((zh.flags & ZTrailInfo) && (hcrc || hcsize || huncsize)){ | 
|  | print("\theader crc %lux\n", zh.crc); | 
|  | print("\theader compressed size %lud\n", zh.csize); | 
|  | print("\theader uncompressed size %lud\n", zh.uncsize); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if(zh.crc != crc) | 
|  | error("crc mismatch for %s", zh.file); | 
|  | if(zh.uncsize != wlen) | 
|  | error("output size mismatch for %s", zh.file); | 
|  | if(zh.csize != rlen) | 
|  | error("input size mismatch for %s", zh.file); | 
|  |  | 
|  |  | 
|  | free(zh.file); | 
|  | zh.file = nil; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * extract files using the info in the central directory structure | 
|  | */ | 
|  | static int | 
|  | unzip(Biobuf *bin, char *file) | 
|  | { | 
|  | ZipHead zh; | 
|  | vlong off; | 
|  | int volatile ok, eok, entries; | 
|  |  | 
|  | entries = findCDir(bin, file); | 
|  | if(entries < 0) | 
|  | return 0; | 
|  |  | 
|  | ok = 1; | 
|  | while(entries-- > 0){ | 
|  | if(setjmp(zjmp)){ | 
|  | free(zh.file); | 
|  | return 0; | 
|  | } | 
|  | memset(&zh, 0, sizeof(zh)); | 
|  | if(!cheader(bin, &zh)) | 
|  | return ok; | 
|  |  | 
|  |  | 
|  | off = Boffset(bin); | 
|  | if(wantFile(zh.file)){ | 
|  | if(Bseek(bin, zh.off, 0) < 0){ | 
|  | fprint(2, "unzip: can't seek to start of %s, skipping\n", zh.file); | 
|  | ok = 0; | 
|  | }else{ | 
|  | eok = unzipEntry(bin, &zh); | 
|  | if(eok <= 0){ | 
|  | fprint(2, "unzip: skipping %s\n", zh.file); | 
|  | ok = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | free(zh.file); | 
|  | zh.file = nil; | 
|  |  | 
|  | if(Bseek(bin, off, 0) < 0){ | 
|  | fprint(2, "unzip: can't seek to start of next entry, terminating extraction\n"); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | return ok; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * extract files using the info the "local file headers" | 
|  | */ | 
|  | static int | 
|  | sunzip(Biobuf *bin) | 
|  | { | 
|  | int eok; | 
|  |  | 
|  | for(;;){ | 
|  | eok = unzipEntry(bin, nil); | 
|  | if(eok == 0) | 
|  | return 1; | 
|  | if(eok < 0) | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | makedir(char *s) | 
|  | { | 
|  | int f; | 
|  |  | 
|  | if (access(s, AEXIST) == 0) | 
|  | return -1; | 
|  | f = create(s, OREAD, DMDIR | 0777); | 
|  | if (f >= 0) | 
|  | close(f); | 
|  | return f; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mkpdirs(char *s) | 
|  | { | 
|  | int done = 0; | 
|  | char *p = s; | 
|  |  | 
|  | while (!done && (p = strchr(p + 1, '/')) != nil) { | 
|  | *p = '\0'; | 
|  | done = (access(s, AEXIST) < 0 && makedir(s) < 0); | 
|  | *p = '/'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * extracts a single entry from a zip file | 
|  | * czh is the optional corresponding central directory entry | 
|  | */ | 
|  | static int | 
|  | unzipEntry(Biobuf *bin, ZipHead *czh) | 
|  | { | 
|  | Dir *d; | 
|  | ZipHead zh; | 
|  | char *p; | 
|  | vlong off; | 
|  | int fd, isdir, ok, err; | 
|  |  | 
|  | zh.file = nil; | 
|  | if(setjmp(zjmp)){ | 
|  | delfile = nil; | 
|  | free(zh.file); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | memset(&zh, 0, sizeof(zh)); | 
|  | if(!header(bin, &zh)) | 
|  | return 0; | 
|  |  | 
|  | ok = 1; | 
|  | isdir = 0; | 
|  |  | 
|  | fd = -1; | 
|  | if(wantFile(zh.file)){ | 
|  | if(verbose) | 
|  | fprint(2, "extracting %s\n", zh.file); | 
|  |  | 
|  | if(czh != nil && czh->extos == ZDos){ | 
|  | isdir = czh->eattr & ZDDir; | 
|  | if(isdir && zh.uncsize != 0) | 
|  | fprint(2, "unzip: ignoring directory data for %s\n", zh.file); | 
|  | } | 
|  | if(zh.meth == 0 && zh.uncsize == 0){ | 
|  | p = strchr(zh.file, '\0'); | 
|  | if(p > zh.file && p[-1] == '/') | 
|  | isdir = 1; | 
|  | } | 
|  |  | 
|  | if(stdout){ | 
|  | if(ok && !isdir) | 
|  | fd = 1; | 
|  | }else if(isdir){ | 
|  | fd = create(zh.file, OREAD, DMDIR | 0775); | 
|  | if(fd < 0){ | 
|  | mkpdirs(zh.file); | 
|  | fd = create(zh.file, OREAD, DMDIR | 0775); | 
|  | } | 
|  | if(fd < 0){ | 
|  | d = dirstat(zh.file); | 
|  | if(d == nil || (d->mode & DMDIR) != DMDIR){ | 
|  | fprint(2, "unzip: can't create directory %s: %r\n", zh.file); | 
|  | ok = 0; | 
|  | } | 
|  | free(d); | 
|  | } | 
|  | }else if(ok){ | 
|  | fd = create(zh.file, OWRITE, 0664); | 
|  | if(fd < 0){ | 
|  | mkpdirs(zh.file); | 
|  | fd = create(zh.file, OWRITE, 0664); | 
|  | } | 
|  | if(fd < 0){ | 
|  | fprint(2, "unzip: can't create %s: %r\n", zh.file); | 
|  | ok = 0; | 
|  | }else | 
|  | delfile = zh.file; | 
|  | } | 
|  | } | 
|  |  | 
|  | wlen = 0; | 
|  | rlen = 0; | 
|  | crc = 0; | 
|  | wbad = 0; | 
|  |  | 
|  | if(zh.meth == 0){ | 
|  | if(!copyout(fd, bin, zh.csize)) | 
|  | error("copying data for %s failed: %r", zh.file); | 
|  | }else if(zh.meth == 8){ | 
|  | off = Boffset(bin); | 
|  | err = inflate((void*)(uintptr)fd, crcwrite, bin, (int(*)(void*))Bgetc); | 
|  | if(err != FlateOk) | 
|  | error("inflate failed: %s", flateerr(err)); | 
|  | rlen = Boffset(bin) - off; | 
|  | }else | 
|  | error("can't handle compression method %d for %s", zh.meth, zh.file); | 
|  |  | 
|  | trailer(bin, &zh); | 
|  |  | 
|  | if(zh.crc != crc) | 
|  | error("crc mismatch for %s", zh.file); | 
|  | if(zh.uncsize != wlen) | 
|  | error("output size mismatch for %s", zh.file); | 
|  | if(zh.csize != rlen) | 
|  | error("input size mismatch for %s", zh.file); | 
|  |  | 
|  | delfile = nil; | 
|  | free(zh.file); | 
|  |  | 
|  | if(fd >= 0 && !stdout){ | 
|  | if(settimes){ | 
|  | d = dirfstat(fd); | 
|  | if(d != nil){ | 
|  | d->mtime = msdos2time(zh.modtime, zh.moddate); | 
|  | if(d->mtime) | 
|  | dirfwstat(fd, d); | 
|  | } | 
|  | } | 
|  | close(fd); | 
|  | } | 
|  |  | 
|  | return ok; | 
|  | } | 
|  |  | 
|  | static int | 
|  | wantFile(char *file) | 
|  | { | 
|  | int i, n; | 
|  |  | 
|  | if(nwant == 0) | 
|  | return 1; | 
|  | for(i = 0; i < nwant; i++){ | 
|  | if(strcmp(want[i], file) == 0) | 
|  | return 1; | 
|  | n = strlen(want[i]); | 
|  | if(strncmp(want[i], file, n) == 0 && file[n] == '/') | 
|  | return 1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * find the start of the central directory | 
|  | * returns the number of entries in the directory, | 
|  | * or -1 if there was an error | 
|  | */ | 
|  | static int | 
|  | findCDir(Biobuf *bin, char *file) | 
|  | { | 
|  | vlong ecoff; | 
|  | long off, size; | 
|  | int entries, zclen, dn, ds, de; | 
|  |  | 
|  | ecoff = Bseek(bin, -ZECHeadSize, 2); | 
|  | if(ecoff < 0){ | 
|  | fprint(2, "unzip: can't seek to contents of %s; try adding -s\n", file); | 
|  | return -1; | 
|  | } | 
|  | if(setjmp(zjmp)) | 
|  | return -1; | 
|  |  | 
|  | if(get4(bin) != ZECHeader){ | 
|  | fprint(2, "unzip: bad magic number for contents of %s\n", file); | 
|  | return -1; | 
|  | } | 
|  | dn = get2(bin); | 
|  | ds = get2(bin); | 
|  | de = get2(bin); | 
|  | entries = get2(bin); | 
|  | size = get4(bin); | 
|  | off = get4(bin); | 
|  | zclen = get2(bin); | 
|  | while(zclen-- > 0) | 
|  | get1(bin); | 
|  |  | 
|  | if(verbose > 1){ | 
|  | print("table starts at %ld for %ld bytes\n", off, size); | 
|  | if(ecoff - size != off) | 
|  | print("\ttable should start at %lld-%ld=%lld\n", ecoff, size, ecoff-size); | 
|  | if(dn || ds || de != entries) | 
|  | print("\tcurrent disk=%d start disk=%d table entries on this disk=%d\n", dn, ds, de); | 
|  | } | 
|  |  | 
|  | if(Bseek(bin, off, 0) != off){ | 
|  | fprint(2, "unzip: can't seek to start of contents of %s\n", file); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return entries; | 
|  | } | 
|  |  | 
|  | static int | 
|  | cheader(Biobuf *bin, ZipHead *zh) | 
|  | { | 
|  | u32int v; | 
|  | int flen, xlen, fclen; | 
|  |  | 
|  | v = get4(bin); | 
|  | if(v != ZCHeader){ | 
|  | if(v == ZECHeader) | 
|  | return 0; | 
|  | error("bad magic number %lux", v); | 
|  | } | 
|  | zh->madevers = get1(bin); | 
|  | zh->madeos = get1(bin); | 
|  | zh->extvers = get1(bin); | 
|  | zh->extos = get1(bin); | 
|  | zh->flags = get2(bin); | 
|  | zh->meth = get2(bin); | 
|  | zh->modtime = get2(bin); | 
|  | zh->moddate = get2(bin); | 
|  | zh->crc = get4(bin); | 
|  | zh->csize = get4(bin); | 
|  | zh->uncsize = get4(bin); | 
|  | flen = get2(bin); | 
|  | xlen = get2(bin); | 
|  | fclen = get2(bin); | 
|  | get2(bin);		/* disk number start */ | 
|  | zh->iattr = get2(bin); | 
|  | zh->eattr = get4(bin); | 
|  | zh->off = get4(bin); | 
|  |  | 
|  | zh->file = getname(bin, flen); | 
|  |  | 
|  | while(xlen-- > 0) | 
|  | get1(bin); | 
|  |  | 
|  | while(fclen-- > 0) | 
|  | get1(bin); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | header(Biobuf *bin, ZipHead *zh) | 
|  | { | 
|  | u32int v; | 
|  | int flen, xlen; | 
|  |  | 
|  | v = get4(bin); | 
|  | if(v != ZHeader){ | 
|  | if(v == ZCHeader) | 
|  | return 0; | 
|  | error("bad magic number %lux at %lld", v, Boffset(bin)-4); | 
|  | } | 
|  | zh->extvers = get1(bin); | 
|  | zh->extos = get1(bin); | 
|  | zh->flags = get2(bin); | 
|  | zh->meth = get2(bin); | 
|  | zh->modtime = get2(bin); | 
|  | zh->moddate = get2(bin); | 
|  | zh->crc = get4(bin); | 
|  | zh->csize = get4(bin); | 
|  | zh->uncsize = get4(bin); | 
|  | flen = get2(bin); | 
|  | xlen = get2(bin); | 
|  |  | 
|  | zh->file = getname(bin, flen); | 
|  |  | 
|  | while(xlen-- > 0) | 
|  | get1(bin); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | trailer(Biobuf *bin, ZipHead *zh) | 
|  | { | 
|  | if(zh->flags & ZTrailInfo){ | 
|  | zh->crc = get4(bin); | 
|  | zh->csize = get4(bin); | 
|  | zh->uncsize = get4(bin); | 
|  | } | 
|  | } | 
|  |  | 
|  | static char* | 
|  | getname(Biobuf *bin, int len) | 
|  | { | 
|  | char *s; | 
|  | int i, c; | 
|  |  | 
|  | s = emalloc(len + 1); | 
|  | for(i = 0; i < len; i++){ | 
|  | c = get1(bin); | 
|  | if(lower) | 
|  | c = tolower(c); | 
|  | s[i] = c; | 
|  | } | 
|  | s[i] = '\0'; | 
|  | return s; | 
|  | } | 
|  |  | 
|  | static int | 
|  | crcwrite(void *out, void *buf, int n) | 
|  | { | 
|  | int fd, nw; | 
|  |  | 
|  | wlen += n; | 
|  | crc = blockcrc(crctab, crc, buf, n); | 
|  | fd = (int)(uintptr)out; | 
|  | if(fd < 0) | 
|  | return n; | 
|  | nw = write(fd, buf, n); | 
|  | if(nw != n) | 
|  | wbad = 1; | 
|  | return nw; | 
|  | } | 
|  |  | 
|  | static int | 
|  | copyout(int ofd, Biobuf *bin, long len) | 
|  | { | 
|  | char buf[BufSize]; | 
|  | int n; | 
|  |  | 
|  | for(; len > 0; len -= n){ | 
|  | n = len; | 
|  | if(n > BufSize) | 
|  | n = BufSize; | 
|  | n = Bread(bin, buf, n); | 
|  | if(n <= 0) | 
|  | return 0; | 
|  | rlen += n; | 
|  | if(crcwrite((void*)(uintptr)ofd, buf, n) != n) | 
|  | return 0; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static u32int | 
|  | get4(Biobuf *b) | 
|  | { | 
|  | u32int v; | 
|  | int i, c; | 
|  |  | 
|  | v = 0; | 
|  | for(i = 0; i < 4; i++){ | 
|  | c = Bgetc(b); | 
|  | if(c < 0) | 
|  | error("unexpected eof reading file information"); | 
|  | v |= c << (i * 8); | 
|  | } | 
|  | return v; | 
|  | } | 
|  |  | 
|  | static int | 
|  | get2(Biobuf *b) | 
|  | { | 
|  | int i, c, v; | 
|  |  | 
|  | v = 0; | 
|  | for(i = 0; i < 2; i++){ | 
|  | c = Bgetc(b); | 
|  | if(c < 0) | 
|  | error("unexpected eof reading file information"); | 
|  | v |= c << (i * 8); | 
|  | } | 
|  | return v; | 
|  | } | 
|  |  | 
|  | static int | 
|  | get1(Biobuf *b) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | c = Bgetc(b); | 
|  | if(c < 0) | 
|  | error("unexpected eof reading file information"); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static long | 
|  | msdos2time(int time, int date) | 
|  | { | 
|  | Tm tm; | 
|  |  | 
|  | tm.hour = time >> 11; | 
|  | tm.min = (time >> 5) & 63; | 
|  | tm.sec = (time & 31) << 1; | 
|  | tm.year = 80 + (date >> 9); | 
|  | tm.mon = ((date >> 5) & 15) - 1; | 
|  | tm.mday = date & 31; | 
|  | tm.zone[0] = '\0'; | 
|  | tm.yday = 0; | 
|  |  | 
|  | return tm2sec(&tm); | 
|  | } | 
|  |  | 
|  | static void* | 
|  | emalloc(u32int n) | 
|  | { | 
|  | void *p; | 
|  |  | 
|  | p = malloc(n); | 
|  | if(p == nil) | 
|  | sysfatal("out of memory"); | 
|  | return p; | 
|  | } | 
|  |  | 
|  | static void | 
|  | error(char *fmt, ...) | 
|  | { | 
|  | va_list arg; | 
|  |  | 
|  | fprint(2, "unzip: "); | 
|  | va_start(arg, fmt); | 
|  | vfprint(2, fmt, arg); | 
|  | va_end(arg); | 
|  | fprint(2, "\n"); | 
|  |  | 
|  | if(delfile != nil){ | 
|  | fprint(2, "unzip: removing output file %s\n", delfile); | 
|  | remove(delfile); | 
|  | delfile = nil; | 
|  | } | 
|  |  | 
|  | longjmp(zjmp, 1); | 
|  | } |