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
usage(void)40 usage(void)
41 {
42 fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
43 exits("usage");
44 }
45
46 void
main(int argc,char * argv[])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
gunzipf(char * file,int stdout)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
gunzip(int ofd,char * ofile,Biobuf * bin)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
header(Biobuf * bin,GZHead * h)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
trailer(Biobuf * bin,long wlen)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
get4(Biobuf * b)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
get1(Biobuf * b)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
crcwrite(void * out,void * buf,int n)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
error(char * fmt,...)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 }
360