| #include <u.h> | 
 | #include <libc.h> | 
 | #include <bio.h> | 
 | #include <flate.h> | 
 | #include "zip.h" | 
 |  | 
 | enum | 
 | { | 
 | 	HeadAlloc	= 64 | 
 | }; | 
 |  | 
 | static	void	zip(Biobuf *bout, char *file, int stdout); | 
 | static	void	zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout); | 
 | static	int	crcread(void *fd, void *buf, int n); | 
 | static	int	zwrite(void *bout, void *buf, int n); | 
 | static	void	put4(Biobuf *b, u32int v); | 
 | static	void	put2(Biobuf *b, int v); | 
 | static	void	put1(Biobuf *b, int v); | 
 | static	void	header(Biobuf *bout, ZipHead *zh); | 
 | static	void	trailer(Biobuf *bout, ZipHead *zh, vlong off); | 
 | static	void	putCDir(Biobuf *bout); | 
 |  | 
 | static	void	error(char*, ...); | 
 | /* #pragma	varargck	argpos	error	1 */ | 
 |  | 
 | static	Biobuf	bout; | 
 | static	u32int	crc; | 
 | static	u32int	*crctab; | 
 | static	int	debug; | 
 | static	int	eof; | 
 | static	int	level; | 
 | static	int	nzheads; | 
 | static	u32int	totr; | 
 | static	u32int	totw; | 
 | static	int	verbose; | 
 | static	int	zhalloc; | 
 | static	ZipHead	*zheads; | 
 | static	jmp_buf	zjmp; | 
 |  | 
 | void | 
 | usage(void) | 
 | { | 
 | 	fprint(2, "usage: zip [-vD] [-1-9] [-f zipfile] file ...\n"); | 
 | 	exits("usage"); | 
 | } | 
 |  | 
 | void | 
 | main(int volatile argc, char **volatile argv) | 
 | { | 
 | 	char *volatile zfile; | 
 | 	int i, fd, err; | 
 |  | 
 | 	zfile = nil; | 
 | 	level = 6; | 
 | 	ARGBEGIN{ | 
 | 	case 'D': | 
 | 		debug++; | 
 | 		break; | 
 | 	case 'f': | 
 | 		zfile = ARGF(); | 
 | 		if(zfile == nil) | 
 | 			usage(); | 
 | 		break; | 
 | 	case 'v': | 
 | 		verbose++; | 
 | 		break; | 
 | 	case '1': case '2': case '3': case '4': | 
 | 	case '5': case '6': case '7': case '8': case '9': | 
 | 		level = ARGC() - '0'; | 
 | 		break; | 
 | 	default: | 
 | 		usage(); | 
 | 		break; | 
 | 	}ARGEND | 
 |  | 
 | 	if(argc == 0) | 
 | 		usage(); | 
 |  | 
 | 	crctab = mkcrctab(ZCrcPoly); | 
 | 	err = deflateinit(); | 
 | 	if(err != FlateOk) | 
 | 		sysfatal("deflateinit failed: %s\n", flateerr(err)); | 
 |  | 
 | 	if(zfile == nil) | 
 | 		fd = 1; | 
 | 	else{ | 
 | 		fd = create(zfile, OWRITE, 0664); | 
 | 		if(fd < 0) | 
 | 			sysfatal("can't create %s: %r\n", zfile); | 
 | 	} | 
 | 	Binit(&bout, fd, OWRITE); | 
 |  | 
 | 	if(setjmp(zjmp)){ | 
 | 		if(zfile != nil){ | 
 | 			fprint(2, "zip: removing output file %s\n", zfile); | 
 | 			remove(zfile); | 
 | 		} | 
 | 		exits("errors"); | 
 | 	} | 
 |  | 
 | 	for(i = 0; i < argc; i++) | 
 | 		zip(&bout, argv[i], zfile == nil); | 
 |  | 
 | 	putCDir(&bout); | 
 |  | 
 | 	exits(nil); | 
 | } | 
 |  | 
 | static void | 
 | zip(Biobuf *bout, char *file, int stdout) | 
 | { | 
 | 	Tm *t; | 
 | 	ZipHead *zh; | 
 | 	Dir *dir; | 
 | 	vlong off; | 
 | 	int fd, err; | 
 |  | 
 | 	fd = open(file, OREAD); | 
 | 	if(fd < 0) | 
 | 		error("can't open %s: %r", file); | 
 | 	dir = dirfstat(fd); | 
 | 	if(dir == nil) | 
 | 		error("can't stat %s: %r", file); | 
 |  | 
 | 	/* | 
 | 	 * create the header | 
 | 	 */ | 
 | 	if(nzheads >= zhalloc){ | 
 | 		zhalloc += HeadAlloc; | 
 | 		zheads = realloc(zheads, zhalloc * sizeof(ZipHead)); | 
 | 		if(zheads == nil) | 
 | 			error("out of memory"); | 
 | 	} | 
 | 	zh = &zheads[nzheads++]; | 
 | 	zh->madeos = ZDos; | 
 | 	zh->madevers = (2 * 10) + 0; | 
 | 	zh->extos = ZDos; | 
 | 	zh->extvers = (2 * 10) + 0; | 
 | 	 | 
 | 	t = localtime(dir->mtime); | 
 | 	zh->modtime = (t->hour<<11) | (t->min<<5) | (t->sec>>1); | 
 | 	zh->moddate = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; | 
 |  | 
 | 	zh->flags = 0; | 
 | 	zh->crc = 0; | 
 | 	zh->csize = 0; | 
 | 	zh->uncsize = 0; | 
 | 	zh->file = strdup(file); | 
 | 	if(zh->file == nil) | 
 | 		error("out of memory"); | 
 | 	zh->iattr = 0; | 
 | 	zh->eattr = ZDArch; | 
 | 	if((dir->mode & 0700) == 0) | 
 | 		zh->eattr |= ZDROnly; | 
 | 	zh->off = Boffset(bout); | 
 |  | 
 | 	if(dir->mode & DMDIR){ | 
 | 		zh->eattr |= ZDDir; | 
 | 		zh->meth = 0; | 
 | 		zipDir(bout, fd, zh, stdout); | 
 | 	}else{ | 
 | 		zh->meth = 8; | 
 | 		if(stdout) | 
 | 			zh->flags |= ZTrailInfo; | 
 | 		off = Boffset(bout); | 
 | 		header(bout, zh); | 
 |  | 
 | 		crc = 0; | 
 | 		eof = 0; | 
 | 		totr = 0; | 
 | 		totw = 0; | 
 | 		err = deflate(bout, zwrite, (void*)(uintptr)fd, crcread, level, debug); | 
 | 		if(err != FlateOk) | 
 | 			error("deflate failed: %s: %r", flateerr(err)); | 
 |  | 
 | 		zh->csize = totw; | 
 | 		zh->uncsize = totr; | 
 | 		zh->crc = crc; | 
 | 		trailer(bout, zh, off); | 
 | 	} | 
 | 	close(fd); | 
 | 	free(dir); | 
 | } | 
 |  | 
 | static void | 
 | zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout) | 
 | { | 
 | 	Dir *dirs; | 
 | 	char *file, *pfile; | 
 | 	int i, nf, nd; | 
 |  | 
 | 	nf = strlen(zh->file) + 1; | 
 | 	if(strcmp(zh->file, ".") == 0){ | 
 | 		nzheads--; | 
 | 		free(zh->file); | 
 | 		pfile = ""; | 
 | 		nf = 1; | 
 | 	}else{ | 
 | 		nf++; | 
 | 		pfile = malloc(nf); | 
 | 		if(pfile == nil) | 
 | 			error("out of memory"); | 
 | 		snprint(pfile, nf, "%s/", zh->file); | 
 | 		free(zh->file); | 
 | 		zh->file = pfile; | 
 | 		header(bout, zh); | 
 | 	} | 
 |  | 
 | 	nf += 256;	/* plenty of room */ | 
 | 	file = malloc(nf); | 
 | 	if(file == nil) | 
 | 		error("out of memory"); | 
 | 	while((nd = dirread(fd, &dirs)) > 0){ | 
 | 		for(i = 0; i < nd; i++){ | 
 | 			snprint(file, nf, "%s%s", pfile, dirs[i].name); | 
 | 			zip(bout, file, stdout); | 
 | 		} | 
 | 		free(dirs); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | header(Biobuf *bout, ZipHead *zh) | 
 | { | 
 | 	int flen; | 
 |  | 
 | 	if(verbose) | 
 | 		fprint(2, "adding %s\n", zh->file); | 
 | 	put4(bout, ZHeader); | 
 | 	put1(bout, zh->extvers); | 
 | 	put1(bout, zh->extos); | 
 | 	put2(bout, zh->flags); | 
 | 	put2(bout, zh->meth); | 
 | 	put2(bout, zh->modtime); | 
 | 	put2(bout, zh->moddate); | 
 | 	put4(bout, zh->crc); | 
 | 	put4(bout, zh->csize); | 
 | 	put4(bout, zh->uncsize); | 
 | 	flen = strlen(zh->file); | 
 | 	put2(bout, flen); | 
 | 	put2(bout, 0); | 
 | 	if(Bwrite(bout, zh->file, flen) != flen) | 
 | 		error("write error"); | 
 | } | 
 |  | 
 | static void | 
 | trailer(Biobuf *bout, ZipHead *zh, vlong off) | 
 | { | 
 | 	vlong coff; | 
 |  | 
 | 	coff = -1; | 
 | 	if(!(zh->flags & ZTrailInfo)){ | 
 | 		coff = Boffset(bout); | 
 | 		if(Bseek(bout, off + ZHeadCrc, 0) < 0) | 
 | 			error("can't seek in archive"); | 
 | 	} | 
 | 	put4(bout, zh->crc); | 
 | 	put4(bout, zh->csize); | 
 | 	put4(bout, zh->uncsize); | 
 | 	if(!(zh->flags & ZTrailInfo)){ | 
 | 		if(Bseek(bout, coff, 0) < 0) | 
 | 			error("can't seek in archive"); | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | cheader(Biobuf *bout, ZipHead *zh) | 
 | { | 
 | 	int flen; | 
 |  | 
 | 	put4(bout, ZCHeader); | 
 | 	put1(bout, zh->madevers); | 
 | 	put1(bout, zh->madeos); | 
 | 	put1(bout, zh->extvers); | 
 | 	put1(bout, zh->extos); | 
 | 	put2(bout, zh->flags & ~ZTrailInfo); | 
 | 	put2(bout, zh->meth); | 
 | 	put2(bout, zh->modtime); | 
 | 	put2(bout, zh->moddate); | 
 | 	put4(bout, zh->crc); | 
 | 	put4(bout, zh->csize); | 
 | 	put4(bout, zh->uncsize); | 
 | 	flen = strlen(zh->file); | 
 | 	put2(bout, flen); | 
 | 	put2(bout, 0); | 
 | 	put2(bout, 0); | 
 | 	put2(bout, 0); | 
 | 	put2(bout, zh->iattr); | 
 | 	put4(bout, zh->eattr); | 
 | 	put4(bout, zh->off); | 
 | 	if(Bwrite(bout, zh->file, flen) != flen) | 
 | 		error("write error"); | 
 | } | 
 |  | 
 | static void | 
 | putCDir(Biobuf *bout) | 
 | { | 
 | 	vlong hoff, ecoff; | 
 | 	int i; | 
 |  | 
 | 	hoff = Boffset(bout); | 
 |  | 
 | 	for(i = 0; i < nzheads; i++) | 
 | 		cheader(bout, &zheads[i]); | 
 |  | 
 | 	ecoff = Boffset(bout); | 
 |  | 
 | 	if(nzheads >= (1 << 16)) | 
 | 		error("too many entries in zip file: max %d", (1 << 16) - 1); | 
 | 	put4(bout, ZECHeader); | 
 | 	put2(bout, 0); | 
 | 	put2(bout, 0); | 
 | 	put2(bout, nzheads); | 
 | 	put2(bout, nzheads); | 
 | 	put4(bout, ecoff - hoff); | 
 | 	put4(bout, hoff); | 
 | 	put2(bout, 0); | 
 | } | 
 |  | 
 | static int | 
 | crcread(void *fd, void *buf, int n) | 
 | { | 
 | 	int nr, m; | 
 |  | 
 | 	nr = 0; | 
 | 	for(; !eof && n > 0; n -= m){ | 
 | 		m = read((int)(uintptr)fd, (char*)buf+nr, n); | 
 | 		if(m <= 0){ | 
 | 			eof = 1; | 
 | 			if(m < 0) | 
 | { | 
 | fprint(2, "input error %r\n"); | 
 | 				return -1; | 
 | } | 
 | 			break; | 
 | 		} | 
 | 		nr += m; | 
 | 	} | 
 | 	crc = blockcrc(crctab, crc, buf, nr); | 
 | 	totr += nr; | 
 | 	return nr; | 
 | } | 
 |  | 
 | static int | 
 | zwrite(void *bout, void *buf, int n) | 
 | { | 
 | 	if(n != Bwrite(bout, buf, n)){ | 
 | 		eof = 1; | 
 | 		return -1; | 
 | 	} | 
 | 	totw += n; | 
 | 	return n; | 
 | } | 
 |  | 
 | static void | 
 | put4(Biobuf *b, u32int v) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for(i = 0; i < 4; i++){ | 
 | 		if(Bputc(b, v) < 0) | 
 | 			error("write error"); | 
 | 		v >>= 8; | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | put2(Biobuf *b, int v) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for(i = 0; i < 2; i++){ | 
 | 		if(Bputc(b, v) < 0) | 
 | 			error("write error"); | 
 | 		v >>= 8; | 
 | 	} | 
 | } | 
 |  | 
 | static void | 
 | put1(Biobuf *b, int v) | 
 | { | 
 | 	if(Bputc(b, v)< 0) | 
 | 		error("unexpected eof reading file information"); | 
 | } | 
 |  | 
 | static void | 
 | error(char *fmt, ...) | 
 | { | 
 | 	va_list arg; | 
 |  | 
 | 	fprint(2, "zip: "); | 
 | 	va_start(arg, fmt); | 
 | 	vfprint(2, fmt, arg); | 
 | 	va_end(arg); | 
 | 	fprint(2, "\n"); | 
 |  | 
 | 	longjmp(zjmp, 1); | 
 | } |