xref: /plan9/sys/src/cmd/gzip/gunzip.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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\n", 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 	return 0;
214 }
215 
216 static void
217 header(Biobuf *bin, GZHead *h)
218 {
219 	char *s;
220 	int i, c, flag, ns, nsa;
221 
222 	if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
223 		error("bad gzip file magic");
224 	if(get1(bin) != GZDEFLATE)
225 		error("unknown compression type");
226 
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");
230 
231 	/* mod time */
232 	h->mtime = get4(bin);
233 
234 	/* extra flags */
235 	get1(bin);
236 
237 	/* OS type */
238 	get1(bin);
239 
240 	if(flag & GZFEXTRA)
241 		for(i=get1(bin); i>0; i--)
242 			get1(bin);
243 
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");
258 			}
259 		}
260 		s[ns] = '\0';
261 		h->file = s;
262 	}else
263 		h->file = strdup("<unnamed file>");
264 
265 	/* comment */
266 	if(flag & GZFCOMMENT)
267 		while(get1(bin) != 0)
268 			;
269 
270 	/* crc16 */
271 	if(flag & GZFHCRC){
272 		get1(bin);
273 		get1(bin);
274 	}
275 }
276 
277 static void
278 trailer(Biobuf *bin, long wlen)
279 {
280 	ulong tcrc;
281 	long len;
282 
283 	tcrc = get4(bin);
284 	if(tcrc != crc)
285 		error("crc mismatch");
286 
287 	len = get4(bin);
288 
289 	if(len != wlen)
290 		error("bad output length: expected %lud got %lud", wlen, len);
291 }
292 
293 static ulong
294 get4(Biobuf *b)
295 {
296 	ulong v;
297 	int i, c;
298 
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);
305 	}
306 	return v;
307 }
308 
309 static int
310 get1(Biobuf *b)
311 {
312 	int c;
313 
314 	c = Bgetc(b);
315 	if(c < 0)
316 		error("unexpected eof reading file information");
317 	return c;
318 }
319 
320 static int
321 crcwrite(void *out, void *buf, int n)
322 {
323 	int fd, nw;
324 
325 	wlen += n;
326 	crc = blockcrc(crctab, crc, buf, n);
327 	fd = (int)out;
328 	if(fd < 0)
329 		return n;
330 	nw = write(fd, buf, n);
331 	if(nw != n)
332 		wbad = 1;
333 	return nw;
334 }
335 
336 static void
337 error(char *fmt, ...)
338 {
339 	va_list arg;
340 
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");
351 
352 		if(delfile != nil){
353 			fprint(2, "gunzip: removing output file %s\n", delfile);
354 			remove(delfile);
355 			delfile = nil;
356 		}
357 	}
358 
359 	longjmp(zjmp, 1);
360 }
361