]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/gzip/gunzip.c
merge
[plan9front.git] / sys / src / cmd / gzip / gunzip.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <flate.h>
5 #include "gzip.h"
6
7 typedef struct  GZHead  GZHead;
8
9 struct GZHead
10 {
11         ulong   mtime;
12         char    *file;
13 };
14
15 static  int     crcwrite(void *bout, void *buf, int n);
16 static  int     get1(Biobuf *b);
17 static  ulong   get4(Biobuf *b);
18 static  int     gunzipf(char *file, int stdout);
19 static  int     gunzip(int ofd, char *ofile, Biobuf *bin);
20 static  void    header(Biobuf *bin, GZHead *h);
21 static  void    trailer(Biobuf *bin, long wlen);
22 static  void    error(char*, ...);
23 #pragma varargck        argpos  error   1
24
25 static  Biobuf  bin;
26 static  ulong   crc;
27 static  ulong   *crctab;
28 static  int     debug;
29 static  char    *delfile;
30 static  vlong   gzok;
31 static  char    *infile;
32 static  int     settimes;
33 static  int     table;
34 static  int     verbose;
35 static  int     wbad;
36 static  ulong   wlen;
37 static  jmp_buf zjmp;
38
39 void
40 usage(void)
41 {
42         fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
43         exits("usage");
44 }
45
46 void
47 main(int argc, char *argv[])
48 {
49         int i, ok, stdout;
50
51         stdout = 0;
52         ARGBEGIN{
53         case 'D':
54                 debug++;
55                 break;
56         case 'c':
57                 stdout++;
58                 break;
59         case 't':
60                 table++;
61                 break;
62         case 'T':
63                 settimes++;
64                 break;
65         case 'v':
66                 verbose++;
67                 break;
68         default:
69                 usage();
70                 break;
71         }ARGEND
72
73         crctab = mkcrctab(GZCRCPOLY);
74         ok = inflateinit();
75         if(ok != FlateOk)
76                 sysfatal("inflateinit failed: %s", flateerr(ok));
77
78         if(argc == 0){
79                 Binit(&bin, 0, OREAD);
80                 settimes = 0;
81                 infile = "<stdin>";
82                 ok = gunzip(1, "<stdout>", &bin);
83         }else{
84                 ok = 1;
85                 if(stdout)
86                         settimes = 0;
87                 for(i = 0; i < argc; i++)
88                         ok &= gunzipf(argv[i], stdout);
89         }
90
91         exits(ok ? nil: "errors");
92 }
93
94 static int
95 gunzipf(char *file, int stdout)
96 {
97         char ofile[256], *s;
98         int ofd, ifd, ok;
99
100         infile = file;
101         ifd = open(file, OREAD);
102         if(ifd < 0){
103                 fprint(2, "gunzip: can't open %s: %r\n", file);
104                 return 0;
105         }
106
107         Binit(&bin, ifd, OREAD);
108         if(Bgetc(&bin) != GZMAGIC1 || Bgetc(&bin) != GZMAGIC2 || Bgetc(&bin) != GZDEFLATE){
109                 fprint(2, "gunzip: %s is not a gzip deflate file\n", file);
110                 Bterm(&bin);
111                 close(ifd);
112                 return 0;
113         }
114         Bungetc(&bin);
115         Bungetc(&bin);
116         Bungetc(&bin);
117
118         if(table)
119                 ofd = -1;
120         else if(stdout){
121                 ofd = 1;
122                 strcpy(ofile, "<stdout>");
123         }else{
124                 s = strrchr(file, '/');
125                 if(s != nil)
126                         s++;
127                 else
128                         s = file;
129                 strecpy(ofile, ofile+sizeof ofile, s);
130                 s = strrchr(ofile, '.');
131                 if(s != nil && s != ofile && strcmp(s, ".gz") == 0)
132                         *s = '\0';
133                 else if(s != nil && strcmp(s, ".tgz") == 0)
134                         strcpy(s, ".tar");
135                 else if(strcmp(file, ofile) == 0){
136                         fprint(2, "gunzip: can't overwrite %s\n", file);
137                         Bterm(&bin);
138                         close(ifd);
139                         return 0;
140                 }
141
142                 ofd = create(ofile, OWRITE, 0666);
143                 if(ofd < 0){
144                         fprint(2, "gunzip: can't create %s: %r\n", ofile);
145                         Bterm(&bin);
146                         close(ifd);
147                         return 0;
148                 }
149                 delfile = ofile;
150         }
151
152         wbad = 0;
153         ok = gunzip(ofd, ofile, &bin);
154         Bterm(&bin);
155         close(ifd);
156         if(wbad){
157                 fprint(2, "gunzip: can't write %s: %r\n", ofile);
158                 if(delfile)
159                         remove(delfile);
160         }
161         delfile = nil;
162         if(!stdout && ofd >= 0)
163                 close(ofd);
164         return ok;
165 }
166
167 static int
168 gunzip(int ofd, char *ofile, Biobuf *bin)
169 {
170         Dir *d;
171         GZHead h;
172         int err;
173
174         h.file = nil;
175         gzok = 0;
176         for(;;){
177                 if(Bgetc(bin) < 0)
178                         return 1;
179                 Bungetc(bin);
180
181                 if(setjmp(zjmp))
182                         return 0;
183                 header(bin, &h);
184                 gzok = 0;
185
186                 wlen = 0;
187                 crc = 0;
188
189                 if(!table && verbose)
190                         fprint(2, "extracting %s to %s\n", h.file, ofile);
191
192                 err = inflate((void*)ofd, crcwrite, bin, (int(*)(void*))Bgetc);
193                 if(err != FlateOk)
194                         error("inflate failed: %s", flateerr(err));
195
196                 trailer(bin, wlen);
197
198                 if(table){
199                         if(verbose)
200                                 print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime));
201                         else
202                                 print("%s\n", h.file);
203                 }else if(settimes && h.mtime && (d=dirfstat(ofd)) != nil){
204                         d->mtime = h.mtime;
205                         dirfwstat(ofd, d);
206                         free(d);
207                 }
208
209                 free(h.file);
210                 h.file = nil;
211                 gzok = Boffset(bin);
212         }
213 }
214
215 static void
216 header(Biobuf *bin, GZHead *h)
217 {
218         char *s;
219         int i, c, flag, ns, nsa;
220
221         if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
222                 error("bad gzip file magic");
223         if(get1(bin) != GZDEFLATE)
224                 error("unknown compression type");
225
226         flag = get1(bin);
227         if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC))
228                 fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n");
229
230         /* mod time */
231         h->mtime = get4(bin);
232
233         /* extra flags */
234         get1(bin);
235
236         /* OS type */
237         get1(bin);
238
239         if(flag & GZFEXTRA){
240                 i = get1(bin);
241                 i |= get1(bin)<<8;
242                 for(; i>0; i--)
243                         get1(bin);
244         }
245
246         /* name */
247         if(flag & GZFNAME){
248                 nsa = 32;
249                 ns = 0;
250                 s = malloc(nsa);
251                 if(s == nil)
252                         error("out of memory");
253                 while((c = get1(bin)) != 0){
254                         s[ns++] = c;
255                         if(ns >= nsa){
256                                 nsa += 32;
257                                 s = realloc(s, nsa);
258                                 if(s == nil)
259                                         error("out of memory");
260                         }
261                 }
262                 s[ns] = '\0';
263                 h->file = s;
264         }else
265                 h->file = strdup("<unnamed file>");
266
267         /* comment */
268         if(flag & GZFCOMMENT)
269                 while(get1(bin) != 0)
270                         ;
271
272         /* crc16 */
273         if(flag & GZFHCRC){
274                 get1(bin);
275                 get1(bin);
276         }
277 }
278
279 static void
280 trailer(Biobuf *bin, long wlen)
281 {
282         ulong tcrc;
283         long len;
284
285         tcrc = get4(bin);
286         if(tcrc != crc)
287                 error("crc mismatch");
288
289         len = get4(bin);
290
291         if(len != wlen)
292                 error("bad output length: expected %lud got %lud", wlen, len);
293 }
294
295 static ulong
296 get4(Biobuf *b)
297 {
298         ulong v;
299         int i, c;
300
301         v = 0;
302         for(i = 0; i < 4; i++){
303                 c = Bgetc(b);
304                 if(c < 0)
305                         error("unexpected eof reading file information");
306                 v |= c << (i * 8);
307         }
308         return v;
309 }
310
311 static int
312 get1(Biobuf *b)
313 {
314         int c;
315
316         c = Bgetc(b);
317         if(c < 0)
318                 error("unexpected eof reading file information");
319         return c;
320 }
321
322 static int
323 crcwrite(void *out, void *buf, int n)
324 {
325         int fd, nw;
326
327         wlen += n;
328         crc = blockcrc(crctab, crc, buf, n);
329         fd = (int)(uintptr)out;
330         if(fd < 0)
331                 return n;
332         nw = write(fd, buf, n);
333         if(nw != n)
334                 wbad = 1;
335         return nw;
336 }
337
338 static void
339 error(char *fmt, ...)
340 {
341         va_list arg;
342
343         if(gzok)
344                 fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
345         else{
346                 fprint(2, "gunzip: ");
347                 if(infile)
348                         fprint(2, "%s: ", infile);
349                 va_start(arg, fmt);
350                 vfprint(2, fmt, arg);
351                 va_end(arg);
352                 fprint(2, "\n");
353         
354                 if(delfile != nil){
355                         fprint(2, "gunzip: removing output file %s\n", delfile);
356                         remove(delfile);
357                         delfile = nil;
358                 }
359         }
360
361         longjmp(zjmp, 1);
362 }