xref: /plan9/sys/src/cmd/gzip/gunzip.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
480ee5cbfSDavid du Colombier #include <flate.h>
57dd7cddfSDavid du Colombier #include "gzip.h"
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier typedef struct	GZHead	GZHead;
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier struct GZHead
107dd7cddfSDavid du Colombier {
117dd7cddfSDavid du Colombier 	ulong	mtime;
127dd7cddfSDavid du Colombier 	char	*file;
137dd7cddfSDavid du Colombier };
147dd7cddfSDavid du Colombier 
157dd7cddfSDavid du Colombier static	int	crcwrite(void *bout, void *buf, int n);
167dd7cddfSDavid du Colombier static	int	get1(Biobuf *b);
177dd7cddfSDavid du Colombier static	ulong	get4(Biobuf *b);
187dd7cddfSDavid du Colombier static	int	gunzipf(char *file, int stdout);
197dd7cddfSDavid du Colombier static	int	gunzip(int ofd, char *ofile, Biobuf *bin);
207dd7cddfSDavid du Colombier static	void	header(Biobuf *bin, GZHead *h);
217dd7cddfSDavid du Colombier static	void	trailer(Biobuf *bin, long wlen);
227dd7cddfSDavid du Colombier static	void	error(char*, ...);
237dd7cddfSDavid du Colombier #pragma	varargck	argpos	error	1
247dd7cddfSDavid du Colombier 
257dd7cddfSDavid du Colombier static	Biobuf	bin;
267dd7cddfSDavid du Colombier static	ulong	crc;
2780ee5cbfSDavid du Colombier static	ulong	*crctab;
287dd7cddfSDavid du Colombier static	int	debug;
297dd7cddfSDavid du Colombier static	char	*delfile;
307dd7cddfSDavid du Colombier static	vlong	gzok;
317dd7cddfSDavid du Colombier static	char	*infile;
327dd7cddfSDavid du Colombier static	int	settimes;
337dd7cddfSDavid du Colombier static	int	table;
347dd7cddfSDavid du Colombier static	int	verbose;
357dd7cddfSDavid du Colombier static	int	wbad;
367dd7cddfSDavid du Colombier static	ulong	wlen;
377dd7cddfSDavid du Colombier static	jmp_buf	zjmp;
387dd7cddfSDavid du Colombier 
397dd7cddfSDavid du Colombier void
usage(void)407dd7cddfSDavid du Colombier usage(void)
417dd7cddfSDavid du Colombier {
427dd7cddfSDavid du Colombier 	fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");
437dd7cddfSDavid du Colombier 	exits("usage");
447dd7cddfSDavid du Colombier }
457dd7cddfSDavid du Colombier 
467dd7cddfSDavid du Colombier void
main(int argc,char * argv[])477dd7cddfSDavid du Colombier main(int argc, char *argv[])
487dd7cddfSDavid du Colombier {
497dd7cddfSDavid du Colombier 	int i, ok, stdout;
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier 	stdout = 0;
527dd7cddfSDavid du Colombier 	ARGBEGIN{
537dd7cddfSDavid du Colombier 	case 'D':
547dd7cddfSDavid du Colombier 		debug++;
557dd7cddfSDavid du Colombier 		break;
567dd7cddfSDavid du Colombier 	case 'c':
577dd7cddfSDavid du Colombier 		stdout++;
587dd7cddfSDavid du Colombier 		break;
597dd7cddfSDavid du Colombier 	case 't':
607dd7cddfSDavid du Colombier 		table++;
617dd7cddfSDavid du Colombier 		break;
627dd7cddfSDavid du Colombier 	case 'T':
637dd7cddfSDavid du Colombier 		settimes++;
647dd7cddfSDavid du Colombier 		break;
657dd7cddfSDavid du Colombier 	case 'v':
667dd7cddfSDavid du Colombier 		verbose++;
677dd7cddfSDavid du Colombier 		break;
687dd7cddfSDavid du Colombier 	default:
697dd7cddfSDavid du Colombier 		usage();
707dd7cddfSDavid du Colombier 		break;
717dd7cddfSDavid du Colombier 	}ARGEND
727dd7cddfSDavid du Colombier 
7380ee5cbfSDavid du Colombier 	crctab = mkcrctab(GZCRCPOLY);
7480ee5cbfSDavid du Colombier 	ok = inflateinit();
7580ee5cbfSDavid du Colombier 	if(ok != FlateOk)
76*14cc0f53SDavid du Colombier 		sysfatal("inflateinit failed: %s", flateerr(ok));
777dd7cddfSDavid du Colombier 
787dd7cddfSDavid du Colombier 	if(argc == 0){
797dd7cddfSDavid du Colombier 		Binit(&bin, 0, OREAD);
807dd7cddfSDavid du Colombier 		settimes = 0;
817dd7cddfSDavid du Colombier 		infile = "<stdin>";
827dd7cddfSDavid du Colombier 		ok = gunzip(1, "<stdout>", &bin);
837dd7cddfSDavid du Colombier 	}else{
847dd7cddfSDavid du Colombier 		ok = 1;
857dd7cddfSDavid du Colombier 		if(stdout)
867dd7cddfSDavid du Colombier 			settimes = 0;
877dd7cddfSDavid du Colombier 		for(i = 0; i < argc; i++)
887dd7cddfSDavid du Colombier 			ok &= gunzipf(argv[i], stdout);
897dd7cddfSDavid du Colombier 	}
907dd7cddfSDavid du Colombier 
917dd7cddfSDavid du Colombier 	exits(ok ? nil: "errors");
927dd7cddfSDavid du Colombier }
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier static int
gunzipf(char * file,int stdout)957dd7cddfSDavid du Colombier gunzipf(char *file, int stdout)
967dd7cddfSDavid du Colombier {
979a747e4fSDavid du Colombier 	char ofile[256], *s;
987dd7cddfSDavid du Colombier 	int ofd, ifd, ok;
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier 	infile = file;
1017dd7cddfSDavid du Colombier 	ifd = open(file, OREAD);
1027dd7cddfSDavid du Colombier 	if(ifd < 0){
1037dd7cddfSDavid du Colombier 		fprint(2, "gunzip: can't open %s: %r\n", file);
1047dd7cddfSDavid du Colombier 		return 0;
1057dd7cddfSDavid du Colombier 	}
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier 	Binit(&bin, ifd, OREAD);
1087dd7cddfSDavid du Colombier 	if(Bgetc(&bin) != GZMAGIC1 || Bgetc(&bin) != GZMAGIC2 || Bgetc(&bin) != GZDEFLATE){
1097dd7cddfSDavid du Colombier 		fprint(2, "gunzip: %s is not a gzip deflate file\n", file);
1107dd7cddfSDavid du Colombier 		Bterm(&bin);
1117dd7cddfSDavid du Colombier 		close(ifd);
1127dd7cddfSDavid du Colombier 		return 0;
1137dd7cddfSDavid du Colombier 	}
1147dd7cddfSDavid du Colombier 	Bungetc(&bin);
1157dd7cddfSDavid du Colombier 	Bungetc(&bin);
1167dd7cddfSDavid du Colombier 	Bungetc(&bin);
1177dd7cddfSDavid du Colombier 
1187dd7cddfSDavid du Colombier 	if(table)
1197dd7cddfSDavid du Colombier 		ofd = -1;
1207dd7cddfSDavid du Colombier 	else if(stdout){
1217dd7cddfSDavid du Colombier 		ofd = 1;
1227dd7cddfSDavid du Colombier 		strcpy(ofile, "<stdout>");
1237dd7cddfSDavid du Colombier 	}else{
1247dd7cddfSDavid du Colombier 		s = strrchr(file, '/');
1257dd7cddfSDavid du Colombier 		if(s != nil)
1267dd7cddfSDavid du Colombier 			s++;
1277dd7cddfSDavid du Colombier 		else
1287dd7cddfSDavid du Colombier 			s = file;
1299a747e4fSDavid du Colombier 		strecpy(ofile, ofile+sizeof ofile, s);
1307dd7cddfSDavid du Colombier 		s = strrchr(ofile, '.');
1317dd7cddfSDavid du Colombier 		if(s != nil && s != ofile && strcmp(s, ".gz") == 0)
1327dd7cddfSDavid du Colombier 			*s = '\0';
1337dd7cddfSDavid du Colombier 		else if(s != nil && strcmp(s, ".tgz") == 0)
1347dd7cddfSDavid du Colombier 			strcpy(s, ".tar");
1357dd7cddfSDavid du Colombier 		else if(strcmp(file, ofile) == 0){
1367dd7cddfSDavid du Colombier 			fprint(2, "gunzip: can't overwrite %s\n", file);
1377dd7cddfSDavid du Colombier 			Bterm(&bin);
1387dd7cddfSDavid du Colombier 			close(ifd);
1397dd7cddfSDavid du Colombier 			return 0;
1407dd7cddfSDavid du Colombier 		}
1417dd7cddfSDavid du Colombier 
1427dd7cddfSDavid du Colombier 		ofd = create(ofile, OWRITE, 0666);
1437dd7cddfSDavid du Colombier 		if(ofd < 0){
1447dd7cddfSDavid du Colombier 			fprint(2, "gunzip: can't create %s: %r\n", ofile);
1457dd7cddfSDavid du Colombier 			Bterm(&bin);
1467dd7cddfSDavid du Colombier 			close(ifd);
1477dd7cddfSDavid du Colombier 			return 0;
1487dd7cddfSDavid du Colombier 		}
1497dd7cddfSDavid du Colombier 		delfile = ofile;
1507dd7cddfSDavid du Colombier 	}
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	wbad = 0;
1537dd7cddfSDavid du Colombier 	ok = gunzip(ofd, ofile, &bin);
1547dd7cddfSDavid du Colombier 	Bterm(&bin);
1557dd7cddfSDavid du Colombier 	close(ifd);
1567dd7cddfSDavid du Colombier 	if(wbad){
1577dd7cddfSDavid du Colombier 		fprint(2, "gunzip: can't write %s: %r\n", ofile);
1587dd7cddfSDavid du Colombier 		if(delfile)
1597dd7cddfSDavid du Colombier 			remove(delfile);
1607dd7cddfSDavid du Colombier 	}
1617dd7cddfSDavid du Colombier 	delfile = nil;
1627dd7cddfSDavid du Colombier 	if(!stdout && ofd >= 0)
1637dd7cddfSDavid du Colombier 		close(ofd);
1647dd7cddfSDavid du Colombier 	return ok;
1657dd7cddfSDavid du Colombier }
1667dd7cddfSDavid du Colombier 
1677dd7cddfSDavid du Colombier static int
gunzip(int ofd,char * ofile,Biobuf * bin)1687dd7cddfSDavid du Colombier gunzip(int ofd, char *ofile, Biobuf *bin)
1697dd7cddfSDavid du Colombier {
1709a747e4fSDavid du Colombier 	Dir *d;
1717dd7cddfSDavid du Colombier 	GZHead h;
17280ee5cbfSDavid du Colombier 	int err;
1737dd7cddfSDavid du Colombier 
1747dd7cddfSDavid du Colombier 	h.file = nil;
1757dd7cddfSDavid du Colombier 	gzok = 0;
1767dd7cddfSDavid du Colombier 	for(;;){
1777dd7cddfSDavid du Colombier 		if(Bgetc(bin) < 0)
1787dd7cddfSDavid du Colombier 			return 1;
1797dd7cddfSDavid du Colombier 		Bungetc(bin);
1807dd7cddfSDavid du Colombier 
1817dd7cddfSDavid du Colombier 		if(setjmp(zjmp))
1827dd7cddfSDavid du Colombier 			return 0;
1837dd7cddfSDavid du Colombier 		header(bin, &h);
1847dd7cddfSDavid du Colombier 		gzok = 0;
1857dd7cddfSDavid du Colombier 
1867dd7cddfSDavid du Colombier 		wlen = 0;
1877dd7cddfSDavid du Colombier 		crc = 0;
1887dd7cddfSDavid du Colombier 
1897dd7cddfSDavid du Colombier 		if(!table && verbose)
1907dd7cddfSDavid du Colombier 			fprint(2, "extracting %s to %s\n", h.file, ofile);
1917dd7cddfSDavid du Colombier 
19280ee5cbfSDavid du Colombier 		err = inflate((void*)ofd, crcwrite, bin, (int(*)(void*))Bgetc);
19380ee5cbfSDavid du Colombier 		if(err != FlateOk)
19480ee5cbfSDavid du Colombier 			error("inflate failed: %s", flateerr(err));
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 		trailer(bin, wlen);
1977dd7cddfSDavid du Colombier 
1987dd7cddfSDavid du Colombier 		if(table){
1997dd7cddfSDavid du Colombier 			if(verbose)
2007dd7cddfSDavid du Colombier 				print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime));
2017dd7cddfSDavid du Colombier 			else
2027dd7cddfSDavid du Colombier 				print("%s\n", h.file);
2039a747e4fSDavid du Colombier 		}else if(settimes && h.mtime && (d=dirfstat(ofd)) != nil){
2049a747e4fSDavid du Colombier 			d->mtime = h.mtime;
2059a747e4fSDavid du Colombier 			dirfwstat(ofd, d);
2069a747e4fSDavid du Colombier 			free(d);
2077dd7cddfSDavid du Colombier 		}
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier 		free(h.file);
2107dd7cddfSDavid du Colombier 		h.file = nil;
2117dd7cddfSDavid du Colombier 		gzok = Boffset(bin);
2127dd7cddfSDavid du Colombier 	}
2137dd7cddfSDavid du Colombier }
2147dd7cddfSDavid du Colombier 
2157dd7cddfSDavid du Colombier static void
header(Biobuf * bin,GZHead * h)2167dd7cddfSDavid du Colombier header(Biobuf *bin, GZHead *h)
2177dd7cddfSDavid du Colombier {
2187dd7cddfSDavid du Colombier 	char *s;
2197dd7cddfSDavid du Colombier 	int i, c, flag, ns, nsa;
2207dd7cddfSDavid du Colombier 
2217dd7cddfSDavid du Colombier 	if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)
2227dd7cddfSDavid du Colombier 		error("bad gzip file magic");
2237dd7cddfSDavid du Colombier 	if(get1(bin) != GZDEFLATE)
2247dd7cddfSDavid du Colombier 		error("unknown compression type");
2257dd7cddfSDavid du Colombier 
2267dd7cddfSDavid du Colombier 	flag = get1(bin);
2277dd7cddfSDavid du Colombier 	if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC))
2287dd7cddfSDavid du Colombier 		fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n");
2297dd7cddfSDavid du Colombier 
2307dd7cddfSDavid du Colombier 	/* mod time */
2317dd7cddfSDavid du Colombier 	h->mtime = get4(bin);
2327dd7cddfSDavid du Colombier 
2337dd7cddfSDavid du Colombier 	/* extra flags */
2347dd7cddfSDavid du Colombier 	get1(bin);
2357dd7cddfSDavid du Colombier 
2367dd7cddfSDavid du Colombier 	/* OS type */
2377dd7cddfSDavid du Colombier 	get1(bin);
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier 	if(flag & GZFEXTRA)
2407dd7cddfSDavid du Colombier 		for(i=get1(bin); i>0; i--)
2417dd7cddfSDavid du Colombier 			get1(bin);
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	/* name */
2447dd7cddfSDavid du Colombier 	if(flag & GZFNAME){
2457dd7cddfSDavid du Colombier 		nsa = 32;
2467dd7cddfSDavid du Colombier 		ns = 0;
2477dd7cddfSDavid du Colombier 		s = malloc(nsa);
2487dd7cddfSDavid du Colombier 		if(s == nil)
2497dd7cddfSDavid du Colombier 			error("out of memory");
2507dd7cddfSDavid du Colombier 		while((c = get1(bin)) != 0){
2517dd7cddfSDavid du Colombier 			s[ns++] = c;
2527dd7cddfSDavid du Colombier 			if(ns >= nsa){
2537dd7cddfSDavid du Colombier 				nsa += 32;
2547dd7cddfSDavid du Colombier 				s = realloc(s, nsa);
2557dd7cddfSDavid du Colombier 				if(s == nil)
2567dd7cddfSDavid du Colombier 					error("out of memory");
2577dd7cddfSDavid du Colombier 			}
2587dd7cddfSDavid du Colombier 		}
2597dd7cddfSDavid du Colombier 		s[ns] = '\0';
2607dd7cddfSDavid du Colombier 		h->file = s;
2617dd7cddfSDavid du Colombier 	}else
2627dd7cddfSDavid du Colombier 		h->file = strdup("<unnamed file>");
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier 	/* comment */
2657dd7cddfSDavid du Colombier 	if(flag & GZFCOMMENT)
2667dd7cddfSDavid du Colombier 		while(get1(bin) != 0)
2677dd7cddfSDavid du Colombier 			;
2687dd7cddfSDavid du Colombier 
2697dd7cddfSDavid du Colombier 	/* crc16 */
2707dd7cddfSDavid du Colombier 	if(flag & GZFHCRC){
2717dd7cddfSDavid du Colombier 		get1(bin);
2727dd7cddfSDavid du Colombier 		get1(bin);
2737dd7cddfSDavid du Colombier 	}
2747dd7cddfSDavid du Colombier }
2757dd7cddfSDavid du Colombier 
2767dd7cddfSDavid du Colombier static void
trailer(Biobuf * bin,long wlen)2777dd7cddfSDavid du Colombier trailer(Biobuf *bin, long wlen)
2787dd7cddfSDavid du Colombier {
2797dd7cddfSDavid du Colombier 	ulong tcrc;
2807dd7cddfSDavid du Colombier 	long len;
2817dd7cddfSDavid du Colombier 
2827dd7cddfSDavid du Colombier 	tcrc = get4(bin);
2837dd7cddfSDavid du Colombier 	if(tcrc != crc)
2847dd7cddfSDavid du Colombier 		error("crc mismatch");
2857dd7cddfSDavid du Colombier 
2867dd7cddfSDavid du Colombier 	len = get4(bin);
2877dd7cddfSDavid du Colombier 
2887dd7cddfSDavid du Colombier 	if(len != wlen)
2897dd7cddfSDavid du Colombier 		error("bad output length: expected %lud got %lud", wlen, len);
2907dd7cddfSDavid du Colombier }
2917dd7cddfSDavid du Colombier 
2927dd7cddfSDavid du Colombier static ulong
get4(Biobuf * b)2937dd7cddfSDavid du Colombier get4(Biobuf *b)
2947dd7cddfSDavid du Colombier {
2957dd7cddfSDavid du Colombier 	ulong v;
2967dd7cddfSDavid du Colombier 	int i, c;
2977dd7cddfSDavid du Colombier 
2987dd7cddfSDavid du Colombier 	v = 0;
2997dd7cddfSDavid du Colombier 	for(i = 0; i < 4; i++){
3007dd7cddfSDavid du Colombier 		c = Bgetc(b);
3017dd7cddfSDavid du Colombier 		if(c < 0)
3027dd7cddfSDavid du Colombier 			error("unexpected eof reading file information");
3037dd7cddfSDavid du Colombier 		v |= c << (i * 8);
3047dd7cddfSDavid du Colombier 	}
3057dd7cddfSDavid du Colombier 	return v;
3067dd7cddfSDavid du Colombier }
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier static int
get1(Biobuf * b)3097dd7cddfSDavid du Colombier get1(Biobuf *b)
3107dd7cddfSDavid du Colombier {
3117dd7cddfSDavid du Colombier 	int c;
3127dd7cddfSDavid du Colombier 
3137dd7cddfSDavid du Colombier 	c = Bgetc(b);
3147dd7cddfSDavid du Colombier 	if(c < 0)
3157dd7cddfSDavid du Colombier 		error("unexpected eof reading file information");
3167dd7cddfSDavid du Colombier 	return c;
3177dd7cddfSDavid du Colombier }
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier static int
crcwrite(void * out,void * buf,int n)3207dd7cddfSDavid du Colombier crcwrite(void *out, void *buf, int n)
3217dd7cddfSDavid du Colombier {
3227dd7cddfSDavid du Colombier 	int fd, nw;
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 	wlen += n;
32580ee5cbfSDavid du Colombier 	crc = blockcrc(crctab, crc, buf, n);
32674f16c81SDavid du Colombier 	fd = (int)(uintptr)out;
3277dd7cddfSDavid du Colombier 	if(fd < 0)
3287dd7cddfSDavid du Colombier 		return n;
3297dd7cddfSDavid du Colombier 	nw = write(fd, buf, n);
3307dd7cddfSDavid du Colombier 	if(nw != n)
3317dd7cddfSDavid du Colombier 		wbad = 1;
3327dd7cddfSDavid du Colombier 	return nw;
3337dd7cddfSDavid du Colombier }
3347dd7cddfSDavid du Colombier 
3357dd7cddfSDavid du Colombier static void
error(char * fmt,...)3367dd7cddfSDavid du Colombier error(char *fmt, ...)
3377dd7cddfSDavid du Colombier {
3387dd7cddfSDavid du Colombier 	va_list arg;
3397dd7cddfSDavid du Colombier 
3407dd7cddfSDavid du Colombier 	if(gzok)
3417dd7cddfSDavid du Colombier 		fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok);
3427dd7cddfSDavid du Colombier 	else{
3439a747e4fSDavid du Colombier 		fprint(2, "gunzip: ");
3449a747e4fSDavid du Colombier 		if(infile)
3459a747e4fSDavid du Colombier 			fprint(2, "%s: ", infile);
3467dd7cddfSDavid du Colombier 		va_start(arg, fmt);
3479a747e4fSDavid du Colombier 		vfprint(2, fmt, arg);
3487dd7cddfSDavid du Colombier 		va_end(arg);
3499a747e4fSDavid du Colombier 		fprint(2, "\n");
3507dd7cddfSDavid du Colombier 
3517dd7cddfSDavid du Colombier 		if(delfile != nil){
3527dd7cddfSDavid du Colombier 			fprint(2, "gunzip: removing output file %s\n", delfile);
3537dd7cddfSDavid du Colombier 			remove(delfile);
3547dd7cddfSDavid du Colombier 			delfile = nil;
3557dd7cddfSDavid du Colombier 		}
3567dd7cddfSDavid du Colombier 	}
3577dd7cddfSDavid du Colombier 
3587dd7cddfSDavid du Colombier 	longjmp(zjmp, 1);
3597dd7cddfSDavid du Colombier }
360