xref: /plan9/sys/src/cmd/gzip/zip.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
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, ulong 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	ulong	crc;
28 static	ulong	*crctab;
29 static	int	debug;
30 static	int	eof;
31 static	int	level;
32 static	int	nzheads;
33 static	ulong	totr;
34 static	ulong	totw;
35 static	int	verbose;
36 static	int	zhalloc;
37 static	ZipHead	*zheads;
38 static	jmp_buf	zjmp;
39 
40 void
usage(void)41 usage(void)
42 {
43 	fprint(2, "usage: zip [-vD] [-1-9] [-f zipfile] file ...\n");
44 	exits("usage");
45 }
46 
47 void
main(int argc,char * argv[])48 main(int argc, char *argv[])
49 {
50 	char *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", 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", 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
zip(Biobuf * bout,char * file,int stdout)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*)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
zipDir(Biobuf * bout,int fd,ZipHead * zh,int stdout)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
header(Biobuf * bout,ZipHead * zh)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
trailer(Biobuf * bout,ZipHead * zh,vlong off)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
cheader(Biobuf * bout,ZipHead * zh)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
putCDir(Biobuf * bout)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
crcread(void * fd,void * buf,int n)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
zwrite(void * bout,void * buf,int n)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
put4(Biobuf * b,ulong v)356 put4(Biobuf *b, ulong 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
put2(Biobuf * b,int v)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
put1(Biobuf * b,int v)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
error(char * fmt,...)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 }
399