]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/gzip/zip.c
merge
[plan9front.git] / sys / src / cmd / gzip / zip.c
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 }