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 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*)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 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 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 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 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 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 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