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