xref: /plan9/sys/src/cmd/gzip/gunzip.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
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