| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include "bzlib.h" |
| |
| static Biobuf bin; |
| static int debug; |
| static int verbose; |
| static char *delfile; |
| static char *infile; |
| static int bunzipf(char *file, int stdout); |
| static int bunzip(int ofd, char *ofile, Biobuf *bin); |
| |
| void |
| usage(void) |
| { |
| fprint(2, "usage: bunzip2 [-cvD] [file ...]\n"); |
| exits("usage"); |
| } |
| |
| void |
| main(int argc, char **argv) |
| { |
| int i, ok, stdout; |
| |
| stdout = 0; |
| ARGBEGIN{ |
| case 'D': |
| debug++; |
| break; |
| case 'c': |
| stdout++; |
| break; |
| case 'v': |
| verbose++; |
| break; |
| }ARGEND |
| |
| if(argc == 0){ |
| Binit(&bin, 0, OREAD); |
| infile = "<stdin>"; |
| ok = bunzip(1, "<stdout>", &bin); |
| }else{ |
| ok = 1; |
| for(i = 0; i < argc; i++) |
| ok &= bunzipf(argv[i], stdout); |
| } |
| |
| exits(ok ? nil: "errors"); |
| } |
| |
| static int |
| bunzipf(char *file, int stdout) |
| { |
| char ofile[64], *s; |
| int ofd, ifd, ok; |
| |
| infile = file; |
| ifd = open(file, OREAD); |
| if(ifd < 0){ |
| fprint(2, "gunzip: can't open %s: %r\n", file); |
| return 0; |
| } |
| |
| Binit(&bin, ifd, OREAD); |
| if(Bgetc(&bin) != 'B' || Bgetc(&bin) != 'Z' || Bgetc(&bin) != 'h'){ |
| fprint(2, "bunzip2: %s is not a bzip2 file\n", file); |
| Bterm(&bin); |
| close(ifd); |
| return 0; |
| } |
| Bungetc(&bin); |
| Bungetc(&bin); |
| Bungetc(&bin); |
| |
| if(stdout){ |
| ofd = 1; |
| strcpy(ofile, "<stdout>"); |
| }else{ |
| s = strrchr(file, '/'); |
| if(s != nil) |
| s++; |
| else |
| s = file; |
| strecpy(ofile, ofile+sizeof ofile, s); |
| s = strrchr(ofile, '.'); |
| if(s != nil && s != ofile && strcmp(s, ".bz2") == 0) |
| *s = '\0'; |
| else if(s != nil && (strcmp(s, ".tbz") == 0 || strcmp(s, ".tbz2") == 0)) |
| strcpy(s, ".tar"); |
| else if(strcmp(file, ofile) == 0){ |
| fprint(2, "bunzip2: can't overwrite %s\n", file); |
| Bterm(&bin); |
| close(ifd); |
| return 0; |
| } |
| |
| ofd = create(ofile, OWRITE, 0666); |
| if(ofd < 0){ |
| fprint(2, "bunzip2: can't create %s: %r\n", ofile); |
| Bterm(&bin); |
| close(ifd); |
| return 0; |
| } |
| delfile = ofile; |
| } |
| |
| ok = bunzip(ofd, ofile, &bin); |
| Bterm(&bin); |
| close(ifd); |
| if(!ok){ |
| fprint(2, "bunzip2: can't write %s: %r\n", ofile); |
| if(delfile) |
| remove(delfile); |
| } |
| delfile = nil; |
| if(!stdout && ofd >= 0) |
| close(ofd); |
| return ok; |
| } |
| |
| static int |
| bunzip(int ofd, char *ofile, Biobuf *bin) |
| { |
| int e, n, done, onemore; |
| char buf[8192]; |
| char obuf[8192]; |
| Biobuf bout; |
| bz_stream strm; |
| |
| USED(ofile); |
| |
| memset(&strm, 0, sizeof strm); |
| BZ2_bzDecompressInit(&strm, verbose, 0); |
| |
| strm.next_in = buf; |
| strm.avail_in = 0; |
| strm.next_out = obuf; |
| strm.avail_out = sizeof obuf; |
| |
| done = 0; |
| Binit(&bout, ofd, OWRITE); |
| |
| /* |
| * onemore is a crummy hack to go 'round the loop |
| * once after we finish, to flush the output buffer. |
| */ |
| onemore = 1; |
| SET(e); |
| do { |
| if(!done && strm.avail_in < sizeof buf) { |
| if(strm.avail_in) |
| memmove(buf, strm.next_in, strm.avail_in); |
| |
| n = Bread(bin, buf+strm.avail_in, sizeof(buf)-strm.avail_in); |
| if(n <= 0) |
| done = 1; |
| else |
| strm.avail_in += n; |
| strm.next_in = buf; |
| } |
| if(strm.avail_out < sizeof obuf) { |
| Bwrite(&bout, obuf, sizeof(obuf)-strm.avail_out); |
| strm.next_out = obuf; |
| strm.avail_out = sizeof obuf; |
| } |
| if(onemore == 0) |
| break; |
| if(strm.avail_in == 0 && strm.avail_out == sizeof obuf) |
| break; |
| } while((e=BZ2_bzDecompress(&strm)) == BZ_OK || onemore--); |
| |
| if(e != BZ_STREAM_END) { |
| fprint(2, "bunzip2: decompress failed\n"); |
| return 0; |
| } |
| |
| if(BZ2_bzDecompressEnd(&strm) != BZ_OK) { |
| fprint(2, "bunzip2: decompress end failed (can't happen)\n"); |
| return 0; |
| } |
| |
| Bterm(&bout); |
| |
| return 1; |
| } |