Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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 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
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
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, 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
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
}