Blob
1 #include <u.h>2 #include <libc.h>3 #include <bio.h>4 #include <flate.h>5 #include "gzip.h"7 typedef struct GZHead GZHead;9 struct GZHead10 {11 ulong mtime;12 char *file;13 };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 */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;39 void40 usage(void)41 {42 fprint(2, "usage: gunzip [-ctvTD] [file ....]\n");43 exits("usage");44 }46 void47 main(int argc, char *argv[])48 {49 int i, ok, stdout;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 }ARGEND73 crctab = mkcrctab(GZCRCPOLY);74 ok = inflateinit();75 if(ok != FlateOk)76 sysfatal("inflateinit failed: %s\n", flateerr(ok));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 }91 exits(ok ? nil: "errors");92 }94 static int95 gunzipf(char *file, int stdout)96 {97 char ofile[256], *s;98 int ofd, ifd, ok;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 }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);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 else128 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 }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 }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 }167 static int168 gunzip(int ofd, char *ofile, Biobuf *bin)169 {170 Dir *d;171 GZHead h;172 int err;174 h.file = nil;175 gzok = 0;176 for(;;){177 if(Bgetc(bin) < 0)178 return 1;179 Bungetc(bin);181 if(setjmp(zjmp))182 return 0;183 header(bin, &h);184 gzok = 0;186 wlen = 0;187 crc = 0;189 if(!table && verbose)190 fprint(2, "extracting %s to %s\n", h.file, ofile);192 err = inflate((void*)ofd, crcwrite, bin, (int(*)(void*))Bgetc);193 if(err != FlateOk)194 error("inflate failed: %s", flateerr(err));196 trailer(bin, wlen);198 if(table){199 if(verbose)200 print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime));201 else202 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 }209 free(h.file);210 h.file = nil;211 gzok = Boffset(bin);212 }213 return 0;214 }216 static void217 header(Biobuf *bin, GZHead *h)218 {219 char *s;220 int i, c, flag, ns, nsa;222 if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2)223 error("bad gzip file magic");224 if(get1(bin) != GZDEFLATE)225 error("unknown compression type");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");231 /* mod time */232 h->mtime = get4(bin);234 /* extra flags */235 get1(bin);237 /* OS type */238 get1(bin);240 if(flag & GZFEXTRA)241 for(i=get1(bin); i>0; i--)242 get1(bin);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 }else263 h->file = strdup("<unnamed file>");265 /* comment */266 if(flag & GZFCOMMENT)267 while(get1(bin) != 0)268 ;270 /* crc16 */271 if(flag & GZFHCRC){272 get1(bin);273 get1(bin);274 }275 }277 static void278 trailer(Biobuf *bin, long wlen)279 {280 ulong tcrc;281 long len;283 tcrc = get4(bin);284 if(tcrc != crc)285 error("crc mismatch");287 len = get4(bin);289 if(len != wlen)290 error("bad output length: expected %lud got %lud", wlen, len);291 }293 static ulong294 get4(Biobuf *b)295 {296 ulong v;297 int i, c;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 }309 static int310 get1(Biobuf *b)311 {312 int c;314 c = Bgetc(b);315 if(c < 0)316 error("unexpected eof reading file information");317 return c;318 }320 static int321 crcwrite(void *out, void *buf, int n)322 {323 int fd, nw;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 }336 static void337 error(char *fmt, ...)338 {339 va_list arg;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");352 if(delfile != nil){353 fprint(2, "gunzip: removing output file %s\n", delfile);354 remove(delfile);355 delfile = nil;356 }357 }359 longjmp(zjmp, 1);360 }