plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

gunzip.c (6075B)


      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 	u32int	mtime;
     12 	char	*file;
     13 };
     14 
     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 */
     24 
     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;
     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*)(uintptr)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 	u32int 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 u32int
    294 get4(Biobuf *b)
    295 {
    296 	u32int 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)(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;
    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 }