plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

zip.c (7108B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <flate.h>
      5 #include "zip.h"
      6 
      7 enum
      8 {
      9 	HeadAlloc	= 64
     10 };
     11 
     12 static	void	zip(Biobuf *bout, char *file, int stdout);
     13 static	void	zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout);
     14 static	int	crcread(void *fd, void *buf, int n);
     15 static	int	zwrite(void *bout, void *buf, int n);
     16 static	void	put4(Biobuf *b, u32int v);
     17 static	void	put2(Biobuf *b, int v);
     18 static	void	put1(Biobuf *b, int v);
     19 static	void	header(Biobuf *bout, ZipHead *zh);
     20 static	void	trailer(Biobuf *bout, ZipHead *zh, vlong off);
     21 static	void	putCDir(Biobuf *bout);
     22 
     23 static	void	error(char*, ...);
     24 /* #pragma	varargck	argpos	error	1 */
     25 
     26 static	Biobuf	bout;
     27 static	u32int	crc;
     28 static	u32int	*crctab;
     29 static	int	debug;
     30 static	int	eof;
     31 static	int	level;
     32 static	int	nzheads;
     33 static	u32int	totr;
     34 static	u32int	totw;
     35 static	int	verbose;
     36 static	int	zhalloc;
     37 static	ZipHead	*zheads;
     38 static	jmp_buf	zjmp;
     39 
     40 void
     41 usage(void)
     42 {
     43 	fprint(2, "usage: zip [-vD] [-1-9] [-f zipfile] file ...\n");
     44 	exits("usage");
     45 }
     46 
     47 void
     48 main(int volatile argc, char **volatile argv)
     49 {
     50 	char *volatile zfile;
     51 	int i, fd, err;
     52 
     53 	zfile = nil;
     54 	level = 6;
     55 	ARGBEGIN{
     56 	case 'D':
     57 		debug++;
     58 		break;
     59 	case 'f':
     60 		zfile = ARGF();
     61 		if(zfile == nil)
     62 			usage();
     63 		break;
     64 	case 'v':
     65 		verbose++;
     66 		break;
     67 	case '1': case '2': case '3': case '4':
     68 	case '5': case '6': case '7': case '8': case '9':
     69 		level = ARGC() - '0';
     70 		break;
     71 	default:
     72 		usage();
     73 		break;
     74 	}ARGEND
     75 
     76 	if(argc == 0)
     77 		usage();
     78 
     79 	crctab = mkcrctab(ZCrcPoly);
     80 	err = deflateinit();
     81 	if(err != FlateOk)
     82 		sysfatal("deflateinit failed: %s\n", flateerr(err));
     83 
     84 	if(zfile == nil)
     85 		fd = 1;
     86 	else{
     87 		fd = create(zfile, OWRITE, 0664);
     88 		if(fd < 0)
     89 			sysfatal("can't create %s: %r\n", zfile);
     90 	}
     91 	Binit(&bout, fd, OWRITE);
     92 
     93 	if(setjmp(zjmp)){
     94 		if(zfile != nil){
     95 			fprint(2, "zip: removing output file %s\n", zfile);
     96 			remove(zfile);
     97 		}
     98 		exits("errors");
     99 	}
    100 
    101 	for(i = 0; i < argc; i++)
    102 		zip(&bout, argv[i], zfile == nil);
    103 
    104 	putCDir(&bout);
    105 
    106 	exits(nil);
    107 }
    108 
    109 static void
    110 zip(Biobuf *bout, char *file, int stdout)
    111 {
    112 	Tm *t;
    113 	ZipHead *zh;
    114 	Dir *dir;
    115 	vlong off;
    116 	int fd, err;
    117 
    118 	fd = open(file, OREAD);
    119 	if(fd < 0)
    120 		error("can't open %s: %r", file);
    121 	dir = dirfstat(fd);
    122 	if(dir == nil)
    123 		error("can't stat %s: %r", file);
    124 
    125 	/*
    126 	 * create the header
    127 	 */
    128 	if(nzheads >= zhalloc){
    129 		zhalloc += HeadAlloc;
    130 		zheads = realloc(zheads, zhalloc * sizeof(ZipHead));
    131 		if(zheads == nil)
    132 			error("out of memory");
    133 	}
    134 	zh = &zheads[nzheads++];
    135 	zh->madeos = ZDos;
    136 	zh->madevers = (2 * 10) + 0;
    137 	zh->extos = ZDos;
    138 	zh->extvers = (2 * 10) + 0;
    139 
    140 	t = localtime(dir->mtime);
    141 	zh->modtime = (t->hour<<11) | (t->min<<5) | (t->sec>>1);
    142 	zh->moddate = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday;
    143 
    144 	zh->flags = 0;
    145 	zh->crc = 0;
    146 	zh->csize = 0;
    147 	zh->uncsize = 0;
    148 	zh->file = strdup(file);
    149 	if(zh->file == nil)
    150 		error("out of memory");
    151 	zh->iattr = 0;
    152 	zh->eattr = ZDArch;
    153 	if((dir->mode & 0700) == 0)
    154 		zh->eattr |= ZDROnly;
    155 	zh->off = Boffset(bout);
    156 
    157 	if(dir->mode & DMDIR){
    158 		zh->eattr |= ZDDir;
    159 		zh->meth = 0;
    160 		zipDir(bout, fd, zh, stdout);
    161 	}else{
    162 		zh->meth = 8;
    163 		if(stdout)
    164 			zh->flags |= ZTrailInfo;
    165 		off = Boffset(bout);
    166 		header(bout, zh);
    167 
    168 		crc = 0;
    169 		eof = 0;
    170 		totr = 0;
    171 		totw = 0;
    172 		err = deflate(bout, zwrite, (void*)(uintptr)fd, crcread, level, debug);
    173 		if(err != FlateOk)
    174 			error("deflate failed: %s: %r", flateerr(err));
    175 
    176 		zh->csize = totw;
    177 		zh->uncsize = totr;
    178 		zh->crc = crc;
    179 		trailer(bout, zh, off);
    180 	}
    181 	close(fd);
    182 	free(dir);
    183 }
    184 
    185 static void
    186 zipDir(Biobuf *bout, int fd, ZipHead *zh, int stdout)
    187 {
    188 	Dir *dirs;
    189 	char *file, *pfile;
    190 	int i, nf, nd;
    191 
    192 	nf = strlen(zh->file) + 1;
    193 	if(strcmp(zh->file, ".") == 0){
    194 		nzheads--;
    195 		free(zh->file);
    196 		pfile = "";
    197 		nf = 1;
    198 	}else{
    199 		nf++;
    200 		pfile = malloc(nf);
    201 		if(pfile == nil)
    202 			error("out of memory");
    203 		snprint(pfile, nf, "%s/", zh->file);
    204 		free(zh->file);
    205 		zh->file = pfile;
    206 		header(bout, zh);
    207 	}
    208 
    209 	nf += 256;	/* plenty of room */
    210 	file = malloc(nf);
    211 	if(file == nil)
    212 		error("out of memory");
    213 	while((nd = dirread(fd, &dirs)) > 0){
    214 		for(i = 0; i < nd; i++){
    215 			snprint(file, nf, "%s%s", pfile, dirs[i].name);
    216 			zip(bout, file, stdout);
    217 		}
    218 		free(dirs);
    219 	}
    220 }
    221 
    222 static void
    223 header(Biobuf *bout, ZipHead *zh)
    224 {
    225 	int flen;
    226 
    227 	if(verbose)
    228 		fprint(2, "adding %s\n", zh->file);
    229 	put4(bout, ZHeader);
    230 	put1(bout, zh->extvers);
    231 	put1(bout, zh->extos);
    232 	put2(bout, zh->flags);
    233 	put2(bout, zh->meth);
    234 	put2(bout, zh->modtime);
    235 	put2(bout, zh->moddate);
    236 	put4(bout, zh->crc);
    237 	put4(bout, zh->csize);
    238 	put4(bout, zh->uncsize);
    239 	flen = strlen(zh->file);
    240 	put2(bout, flen);
    241 	put2(bout, 0);
    242 	if(Bwrite(bout, zh->file, flen) != flen)
    243 		error("write error");
    244 }
    245 
    246 static void
    247 trailer(Biobuf *bout, ZipHead *zh, vlong off)
    248 {
    249 	vlong coff;
    250 
    251 	coff = -1;
    252 	if(!(zh->flags & ZTrailInfo)){
    253 		coff = Boffset(bout);
    254 		if(Bseek(bout, off + ZHeadCrc, 0) < 0)
    255 			error("can't seek in archive");
    256 	}
    257 	put4(bout, zh->crc);
    258 	put4(bout, zh->csize);
    259 	put4(bout, zh->uncsize);
    260 	if(!(zh->flags & ZTrailInfo)){
    261 		if(Bseek(bout, coff, 0) < 0)
    262 			error("can't seek in archive");
    263 	}
    264 }
    265 
    266 static void
    267 cheader(Biobuf *bout, ZipHead *zh)
    268 {
    269 	int flen;
    270 
    271 	put4(bout, ZCHeader);
    272 	put1(bout, zh->madevers);
    273 	put1(bout, zh->madeos);
    274 	put1(bout, zh->extvers);
    275 	put1(bout, zh->extos);
    276 	put2(bout, zh->flags & ~ZTrailInfo);
    277 	put2(bout, zh->meth);
    278 	put2(bout, zh->modtime);
    279 	put2(bout, zh->moddate);
    280 	put4(bout, zh->crc);
    281 	put4(bout, zh->csize);
    282 	put4(bout, zh->uncsize);
    283 	flen = strlen(zh->file);
    284 	put2(bout, flen);
    285 	put2(bout, 0);
    286 	put2(bout, 0);
    287 	put2(bout, 0);
    288 	put2(bout, zh->iattr);
    289 	put4(bout, zh->eattr);
    290 	put4(bout, zh->off);
    291 	if(Bwrite(bout, zh->file, flen) != flen)
    292 		error("write error");
    293 }
    294 
    295 static void
    296 putCDir(Biobuf *bout)
    297 {
    298 	vlong hoff, ecoff;
    299 	int i;
    300 
    301 	hoff = Boffset(bout);
    302 
    303 	for(i = 0; i < nzheads; i++)
    304 		cheader(bout, &zheads[i]);
    305 
    306 	ecoff = Boffset(bout);
    307 
    308 	if(nzheads >= (1 << 16))
    309 		error("too many entries in zip file: max %d", (1 << 16) - 1);
    310 	put4(bout, ZECHeader);
    311 	put2(bout, 0);
    312 	put2(bout, 0);
    313 	put2(bout, nzheads);
    314 	put2(bout, nzheads);
    315 	put4(bout, ecoff - hoff);
    316 	put4(bout, hoff);
    317 	put2(bout, 0);
    318 }
    319 
    320 static int
    321 crcread(void *fd, void *buf, int n)
    322 {
    323 	int nr, m;
    324 
    325 	nr = 0;
    326 	for(; !eof && n > 0; n -= m){
    327 		m = read((int)(uintptr)fd, (char*)buf+nr, n);
    328 		if(m <= 0){
    329 			eof = 1;
    330 			if(m < 0)
    331 {
    332 fprint(2, "input error %r\n");
    333 				return -1;
    334 }
    335 			break;
    336 		}
    337 		nr += m;
    338 	}
    339 	crc = blockcrc(crctab, crc, buf, nr);
    340 	totr += nr;
    341 	return nr;
    342 }
    343 
    344 static int
    345 zwrite(void *bout, void *buf, int n)
    346 {
    347 	if(n != Bwrite(bout, buf, n)){
    348 		eof = 1;
    349 		return -1;
    350 	}
    351 	totw += n;
    352 	return n;
    353 }
    354 
    355 static void
    356 put4(Biobuf *b, u32int v)
    357 {
    358 	int i;
    359 
    360 	for(i = 0; i < 4; i++){
    361 		if(Bputc(b, v) < 0)
    362 			error("write error");
    363 		v >>= 8;
    364 	}
    365 }
    366 
    367 static void
    368 put2(Biobuf *b, int v)
    369 {
    370 	int i;
    371 
    372 	for(i = 0; i < 2; i++){
    373 		if(Bputc(b, v) < 0)
    374 			error("write error");
    375 		v >>= 8;
    376 	}
    377 }
    378 
    379 static void
    380 put1(Biobuf *b, int v)
    381 {
    382 	if(Bputc(b, v)< 0)
    383 		error("unexpected eof reading file information");
    384 }
    385 
    386 static void
    387 error(char *fmt, ...)
    388 {
    389 	va_list arg;
    390 
    391 	fprint(2, "zip: ");
    392 	va_start(arg, fmt);
    393 	vfprint(2, fmt, arg);
    394 	va_end(arg);
    395 	fprint(2, "\n");
    396 
    397 	longjmp(zjmp, 1);
    398 }