Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <flate.h>
5 #include "gzip.h"
7 typedef struct GZHead GZHead;
9 struct GZHead
10 {
11 u32int mtime;
12 char *file;
13 };
15 static int crcwrite(void *bout, void *buf, int n);
16 static int get1(Biobuf *b);
17 static u32int 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 */
25 static Biobuf bin;
26 static u32int crc;
27 static u32int *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 u32int wlen;
37 static jmp_buf zjmp;
39 void
40 usage(void)
41 {
42 fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
43 exits("usage");
44 }
46 void
47 main(int argc, char *argv[])
48 {
49 int i, ok, stdout;
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
73 crctab = mkcrctab(GZCRCPOLY);
74 ok = inflateinit();
75 if(ok != FlateOk)
76 sysfatal("inflateinit failed: %s\n", flateerr(ok));
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 }
91 exits(ok ? nil: "errors");
92 }
94 static int
95 gunzipf(char *file, int stdout)
96 {
97 char ofile[256], *s;
98 int ofd, ifd, ok;
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;
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;
114 Bungetc(&bin);
115 Bungetc(&bin);
116 Bungetc(&bin);
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;
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;
149 delfile = ofile;
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);
161 delfile = nil;
162 if(!stdout && ofd >= 0)
163 close(ofd);
164 return ok;
167 static int
168 gunzip(int ofd, char *ofile, Biobuf *bin)
170 Dir *d;
171 GZHead h;
172 int err;
174 h.file = nil;
175 gzok = 0;
176 for(;;){
177 if(Bgetc(bin) < 0)
178 return 1;
179 Bungetc(bin);
181 if(setjmp(zjmp))
182 return 0;
183 header(bin, &h);
184 gzok = 0;
186 wlen = 0;
187 crc = 0;
189 if(!table && verbose)
190 fprint(2, "extracting %s to %s\n", h.file, ofile);
192 err = inflate((void*)(uintptr)ofd, crcwrite, bin, (int(*)(void*))Bgetc);
193 if(err != FlateOk)
194 error("inflate failed: %s", flateerr(err));
196 trailer(bin, wlen);
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);
209 free(h.file);
210 h.file = nil;
211 gzok = Boffset(bin);
213 /* return 0; */
216 static void
217 header(Biobuf *bin, GZHead *h)
219 char *s;
220 int i, c, flag, ns, nsa;
222 if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
223 error("bad gzip file magic");
224 if(get1(bin) != GZDEFLATE)
225 error("unknown compression type");
227 flag = get1(bin);
228 if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC))
229 fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n");
231 /* mod time */
232 h->mtime = get4(bin);
234 /* extra flags */
235 get1(bin);
237 /* OS type */
238 get1(bin);
240 if(flag & GZFEXTRA)
241 for(i=get1(bin); i>0; i--)
242 get1(bin);
244 /* name */
245 if(flag & GZFNAME){
246 nsa = 32;
247 ns = 0;
248 s = malloc(nsa);
249 if(s == nil)
250 error("out of memory");
251 while((c = get1(bin)) != 0){
252 s[ns++] = c;
253 if(ns >= nsa){
254 nsa += 32;
255 s = realloc(s, nsa);
256 if(s == nil)
257 error("out of memory");
260 s[ns] = '\0';
261 h->file = s;
262 }else
263 h->file = strdup("<unnamed file>");
265 /* comment */
266 if(flag & GZFCOMMENT)
267 while(get1(bin) != 0)
270 /* crc16 */
271 if(flag & GZFHCRC){
272 get1(bin);
273 get1(bin);
277 static void
278 trailer(Biobuf *bin, long wlen)
280 u32int tcrc;
281 long len;
283 tcrc = get4(bin);
284 if(tcrc != crc)
285 error("crc mismatch");
287 len = get4(bin);
289 if(len != wlen)
290 error("bad output length: expected %lud got %lud", wlen, len);
293 static u32int
294 get4(Biobuf *b)
296 u32int v;
297 int i, c;
299 v = 0;
300 for(i = 0; i < 4; i++){
301 c = Bgetc(b);
302 if(c < 0)
303 error("unexpected eof reading file information");
304 v |= c << (i * 8);
306 return v;
309 static int
310 get1(Biobuf *b)
312 int c;
314 c = Bgetc(b);
315 if(c < 0)
316 error("unexpected eof reading file information");
317 return c;
320 static int
321 crcwrite(void *out, void *buf, int n)
323 int fd, nw;
325 wlen += n;
326 crc = blockcrc(crctab, crc, buf, n);
327 fd = (int)(uintptr)out;
328 if(fd < 0)
329 return n;
330 nw = write(fd, buf, n);
331 if(nw != n)
332 wbad = 1;
333 return nw;
336 static void
337 error(char *fmt, ...)
339 va_list arg;
341 if(gzok)
342 fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
343 else{
344 fprint(2, "gunzip: ");
345 if(infile)
346 fprint(2, "%s: ", infile);
347 va_start(arg, fmt);
348 vfprint(2, fmt, arg);
349 va_end(arg);
350 fprint(2, "\n");
352 if(delfile != nil){
353 fprint(2, "gunzip: removing output file %s\n", delfile);
354 remove(delfile);
355 delfile = nil;
359 longjmp(zjmp, 1);