1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include "gzip.h" 5 #include "deflate.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 int debug; 28 static char *delfile; 29 static vlong gzok; 30 static char *infile; 31 static int settimes; 32 static int table; 33 static int verbose; 34 static int wbad; 35 static ulong wlen; 36 static jmp_buf zjmp; 37 38 void 39 usage(void) 40 { 41 fprint(2, "usage: gunzip [-ctvTD] [file ....]\n"); 42 exits("usage"); 43 } 44 45 void 46 main(int argc, char *argv[]) 47 { 48 int i, ok, stdout; 49 50 stdout = 0; 51 ARGBEGIN{ 52 case 'D': 53 debug++; 54 break; 55 case 'c': 56 stdout++; 57 break; 58 case 't': 59 table++; 60 break; 61 case 'T': 62 settimes++; 63 break; 64 case 'v': 65 verbose++; 66 break; 67 default: 68 usage(); 69 break; 70 }ARGEND 71 72 mkcrctab(GZCRCPOLY); 73 inflateinit(); 74 75 if(argc == 0){ 76 Binit(&bin, 0, OREAD); 77 settimes = 0; 78 infile = "<stdin>"; 79 ok = gunzip(1, "<stdout>", &bin); 80 }else{ 81 ok = 1; 82 if(stdout) 83 settimes = 0; 84 for(i = 0; i < argc; i++) 85 ok &= gunzipf(argv[i], stdout); 86 } 87 88 exits(ok ? nil: "errors"); 89 } 90 91 static int 92 gunzipf(char *file, int stdout) 93 { 94 char ofile[NAMELEN], *s; 95 int ofd, ifd, ok; 96 97 infile = file; 98 ifd = open(file, OREAD); 99 if(ifd < 0){ 100 fprint(2, "gunzip: can't open %s: %r\n", file); 101 return 0; 102 } 103 104 Binit(&bin, ifd, OREAD); 105 if(Bgetc(&bin) != GZMAGIC1 || Bgetc(&bin) != GZMAGIC2 || Bgetc(&bin) != GZDEFLATE){ 106 fprint(2, "gunzip: %s is not a gzip deflate file\n", file); 107 Bterm(&bin); 108 close(ifd); 109 return 0; 110 } 111 Bungetc(&bin); 112 Bungetc(&bin); 113 Bungetc(&bin); 114 115 if(table) 116 ofd = -1; 117 else if(stdout){ 118 ofd = 1; 119 strcpy(ofile, "<stdout>"); 120 }else{ 121 s = strrchr(file, '/'); 122 if(s != nil) 123 s++; 124 else 125 s = file; 126 strncpy(ofile, s, NAMELEN); 127 ofile[NAMELEN-1] = '\0'; 128 s = strrchr(ofile, '.'); 129 if(s != nil && s != ofile && strcmp(s, ".gz") == 0) 130 *s = '\0'; 131 else if(s != nil && strcmp(s, ".tgz") == 0) 132 strcpy(s, ".tar"); 133 else if(strcmp(file, ofile) == 0){ 134 fprint(2, "gunzip: can't overwrite %s\n", file); 135 Bterm(&bin); 136 close(ifd); 137 return 0; 138 } 139 140 ofd = create(ofile, OWRITE, 0666); 141 if(ofd < 0){ 142 fprint(2, "gunzip: can't create %s: %r\n", ofile); 143 Bterm(&bin); 144 close(ifd); 145 return 0; 146 } 147 delfile = ofile; 148 } 149 150 wbad = 0; 151 ok = gunzip(ofd, ofile, &bin); 152 Bterm(&bin); 153 close(ifd); 154 if(wbad){ 155 fprint(2, "gunzip: can't write %s: %r\n", ofile); 156 if(delfile) 157 remove(delfile); 158 } 159 delfile = nil; 160 if(!stdout && ofd >= 0) 161 close(ofd); 162 return ok; 163 } 164 165 static int 166 gunzip(int ofd, char *ofile, Biobuf *bin) 167 { 168 Dir d; 169 GZHead h; 170 171 h.file = nil; 172 gzok = 0; 173 for(;;){ 174 if(Bgetc(bin) < 0) 175 return 1; 176 Bungetc(bin); 177 178 if(setjmp(zjmp)) 179 return 0; 180 header(bin, &h); 181 gzok = 0; 182 183 wlen = 0; 184 crc = 0; 185 186 if(!table && verbose) 187 fprint(2, "extracting %s to %s\n", h.file, ofile); 188 189 if(!inflate((void*)ofd, crcwrite, bin, (int(*)(void*))Bgetc)) 190 error("inflate failed: %r"); 191 192 trailer(bin, wlen); 193 194 if(table){ 195 if(verbose) 196 print("%-32s %10ld %s", h.file, wlen, ctime(h.mtime)); 197 else 198 print("%s\n", h.file); 199 }else if(settimes && h.mtime && dirfstat(ofd, &d) >= 0){ 200 d.mtime = h.mtime; 201 dirfwstat(ofd, &d); 202 } 203 204 free(h.file); 205 h.file = nil; 206 gzok = Boffset(bin); 207 } 208 return 0; 209 } 210 211 static void 212 header(Biobuf *bin, GZHead *h) 213 { 214 char *s; 215 int i, c, flag, ns, nsa; 216 217 if(get1(bin) != GZMAGIC1 || get1(bin) != GZMAGIC2) 218 error("bad gzip file magic"); 219 if(get1(bin) != GZDEFLATE) 220 error("unknown compression type"); 221 222 flag = get1(bin); 223 if(flag & ~(GZFTEXT|GZFEXTRA|GZFNAME|GZFCOMMENT|GZFHCRC)) 224 fprint(2, "gunzip: reserved flags set, data may not be decompressed correctly\n"); 225 226 /* mod time */ 227 h->mtime = get4(bin); 228 229 /* extra flags */ 230 get1(bin); 231 232 /* OS type */ 233 get1(bin); 234 235 if(flag & GZFEXTRA) 236 for(i=get1(bin); i>0; i--) 237 get1(bin); 238 239 /* name */ 240 if(flag & GZFNAME){ 241 nsa = 32; 242 ns = 0; 243 s = malloc(nsa); 244 if(s == nil) 245 error("out of memory"); 246 while((c = get1(bin)) != 0){ 247 s[ns++] = c; 248 if(ns >= nsa){ 249 nsa += 32; 250 s = realloc(s, nsa); 251 if(s == nil) 252 error("out of memory"); 253 } 254 } 255 s[ns] = '\0'; 256 h->file = s; 257 }else 258 h->file = strdup("<unnamed file>"); 259 260 /* comment */ 261 if(flag & GZFCOMMENT) 262 while(get1(bin) != 0) 263 ; 264 265 /* crc16 */ 266 if(flag & GZFHCRC){ 267 get1(bin); 268 get1(bin); 269 } 270 } 271 272 static void 273 trailer(Biobuf *bin, long wlen) 274 { 275 ulong tcrc; 276 long len; 277 278 tcrc = get4(bin); 279 if(tcrc != crc) 280 error("crc mismatch"); 281 282 len = get4(bin); 283 284 if(len != wlen) 285 error("bad output length: expected %lud got %lud", wlen, len); 286 } 287 288 static ulong 289 get4(Biobuf *b) 290 { 291 ulong v; 292 int i, c; 293 294 v = 0; 295 for(i = 0; i < 4; i++){ 296 c = Bgetc(b); 297 if(c < 0) 298 error("unexpected eof reading file information"); 299 v |= c << (i * 8); 300 } 301 return v; 302 } 303 304 static int 305 get1(Biobuf *b) 306 { 307 int c; 308 309 c = Bgetc(b); 310 if(c < 0) 311 error("unexpected eof reading file information"); 312 return c; 313 } 314 315 static int 316 crcwrite(void *out, void *buf, int n) 317 { 318 int fd, nw; 319 320 wlen += n; 321 crc = blockcrc(crc, buf, n); 322 fd = (int)out; 323 if(fd < 0) 324 return n; 325 nw = write(fd, buf, n); 326 if(nw != n) 327 wbad = 1; 328 return nw; 329 } 330 331 static void 332 error(char *fmt, ...) 333 { 334 char buf[1024]; 335 va_list arg; 336 337 if(gzok) 338 fprint(2, "gunzip: %s: corrupted data after byte %lld ignored\n", infile, gzok); 339 else{ 340 va_start(arg, fmt); 341 doprint(buf, buf+sizeof(buf), fmt, arg); 342 va_end(arg); 343 if(infile != nil) 344 fprint(2, "gunzip: %s: %s\n", infile, buf); 345 else 346 fprint(2, "gunzip: %s\n", buf); 347 348 if(delfile != nil){ 349 fprint(2, "gunzip: removing output file %s\n", delfile); 350 remove(delfile); 351 delfile = nil; 352 } 353 } 354 355 longjmp(zjmp, 1); 356 } 357