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 }