]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/gzip/gunzip.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[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                 for(i=get1(bin); i>0; i--)
241                         get1(bin);
242
243         /* name */
244         if(flag & GZFNAME){
245                 nsa = 32;
246                 ns = 0;
247                 s = malloc(nsa);
248                 if(s == nil)
249                         error("out of memory");
250                 while((c = get1(bin)) != 0){
251                         s[ns++] = c;
252                         if(ns >= nsa){
253                                 nsa += 32;
254                                 s = realloc(s, nsa);
255                                 if(s == nil)
256                                         error("out of memory");
257                         }
258                 }
259                 s[ns] = '\0';
260                 h->file = s;
261         }else
262                 h->file = strdup("<unnamed file>");
263
264         /* comment */
265         if(flag & GZFCOMMENT)
266                 while(get1(bin) != 0)
267                         ;
268
269         /* crc16 */
270         if(flag & GZFHCRC){
271                 get1(bin);
272                 get1(bin);
273         }
274 }
275
276 static void
277 trailer(Biobuf *bin, long wlen)
278 {
279         ulong tcrc;
280         long len;
281
282         tcrc = get4(bin);
283         if(tcrc != crc)
284                 error("crc mismatch");
285
286         len = get4(bin);
287
288         if(len != wlen)
289                 error("bad output length: expected %lud got %lud", wlen, len);
290 }
291
292 static ulong
293 get4(Biobuf *b)
294 {
295         ulong v;
296         int i, c;
297
298         v = 0;
299         for(i = 0; i < 4; i++){
300                 c = Bgetc(b);
301                 if(c < 0)
302                         error("unexpected eof reading file information");
303                 v |= c << (i * 8);
304         }
305         return v;
306 }
307
308 static int
309 get1(Biobuf *b)
310 {
311         int c;
312
313         c = Bgetc(b);
314         if(c < 0)
315                 error("unexpected eof reading file information");
316         return c;
317 }
318
319 static int
320 crcwrite(void *out, void *buf, int n)
321 {
322         int fd, nw;
323
324         wlen += n;
325         crc = blockcrc(crctab, crc, buf, n);
326         fd = (int)(uintptr)out;
327         if(fd < 0)
328                 return n;
329         nw = write(fd, buf, n);
330         if(nw != n)
331                 wbad = 1;
332         return nw;
333 }
334
335 static void
336 error(char *fmt, ...)
337 {
338         va_list arg;
339
340         if(gzok)
341                 fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
342         else{
343                 fprint(2, "gunzip: ");
344                 if(infile)
345                         fprint(2, "%s: ", infile);
346                 va_start(arg, fmt);
347                 vfprint(2, fmt, arg);
348                 va_end(arg);
349                 fprint(2, "\n");
350         
351                 if(delfile != nil){
352                         fprint(2, "gunzip: removing output file %s\n", delfile);
353                         remove(delfile);
354                         delfile = nil;
355                 }
356         }
357
358         longjmp(zjmp, 1);
359 }