1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include "bzlib.h"
5
6 static Biobuf bin;
7 static int debug;
8 static int verbose;
9 static char *delfile;
10 static char *infile;
11 static int bunzipf(char *file, int stdout);
12 static int bunzip(int ofd, char *ofile, Biobuf *bin);
13
14 void
usage(void)15 usage(void)
16 {
17 fprint(2, "usage: bunzip2 [-cvD] [file ...]\n");
18 exits("usage");
19 }
20
21 void
main(int argc,char ** argv)22 main(int argc, char **argv)
23 {
24 int i, ok, stdout;
25
26 stdout = 0;
27 ARGBEGIN{
28 case 'D':
29 debug++;
30 break;
31 case 'c':
32 stdout++;
33 break;
34 case 'v':
35 verbose++;
36 break;
37 default:
38 usage();
39 }ARGEND
40
41 if(argc == 0){
42 Binit(&bin, 0, OREAD);
43 infile = "<stdin>";
44 ok = bunzip(1, "<stdout>", &bin);
45 }else{
46 ok = 1;
47 for(i = 0; i < argc; i++)
48 ok &= bunzipf(argv[i], stdout);
49 }
50
51 exits(ok ? nil: "errors");
52 }
53
54 static int
bunzipf(char * file,int stdout)55 bunzipf(char *file, int stdout)
56 {
57 char ofile[64], *s;
58 int ofd, ifd, ok;
59
60 infile = file;
61 ifd = open(file, OREAD);
62 if(ifd < 0){
63 fprint(2, "bunzip2: can't open %s: %r\n", file);
64 return 0;
65 }
66
67 Binit(&bin, ifd, OREAD);
68 if(Bgetc(&bin) != 'B' || Bgetc(&bin) != 'Z' || Bgetc(&bin) != 'h'){
69 fprint(2, "bunzip2: %s is not a bzip2 file\n", file);
70 Bterm(&bin);
71 close(ifd);
72 return 0;
73 }
74 Bungetc(&bin);
75 Bungetc(&bin);
76 Bungetc(&bin);
77
78 if(stdout){
79 ofd = 1;
80 strcpy(ofile, "<stdout>");
81 }else{
82 s = strrchr(file, '/');
83 if(s != nil)
84 s++;
85 else
86 s = file;
87 strecpy(ofile, ofile+sizeof ofile, s);
88 s = strrchr(ofile, '.');
89 if(s != nil && s != ofile && strcmp(s, ".bz2") == 0)
90 *s = '\0';
91 else if(s != nil && (strcmp(s, ".tbz") == 0 || strcmp(s, ".tbz2") == 0))
92 strcpy(s, ".tar");
93 else if(strcmp(file, ofile) == 0){
94 fprint(2, "bunzip2: can't overwrite %s\n", file);
95 Bterm(&bin);
96 close(ifd);
97 return 0;
98 }
99
100 ofd = create(ofile, OWRITE, 0666);
101 if(ofd < 0){
102 fprint(2, "bunzip2: can't create %s: %r\n", ofile);
103 Bterm(&bin);
104 close(ifd);
105 return 0;
106 }
107 delfile = ofile;
108 }
109
110 ok = bunzip(ofd, ofile, &bin);
111 Bterm(&bin);
112 close(ifd);
113 if(!ok){
114 fprint(2, "bunzip2: can't write %s: %r\n", ofile);
115 if(delfile)
116 remove(delfile);
117 }
118 delfile = nil;
119 if(!stdout && ofd >= 0)
120 close(ofd);
121 return ok;
122 }
123
124 static int
bunzip(int ofd,char * ofile,Biobuf * bin)125 bunzip(int ofd, char *ofile, Biobuf *bin)
126 {
127 int e, n, done, onemore;
128 char buf[8192];
129 char obuf[8192];
130 Biobuf bout;
131 bz_stream strm;
132
133 USED(ofile);
134
135 memset(&strm, 0, sizeof strm);
136 BZ2_bzDecompressInit(&strm, verbose, 0);
137
138 strm.next_in = buf;
139 strm.avail_in = 0;
140 strm.next_out = obuf;
141 strm.avail_out = sizeof obuf;
142
143 done = 0;
144 Binit(&bout, ofd, OWRITE);
145
146 /*
147 * onemore is a crummy hack to go 'round the loop
148 * once after we finish, to flush the output buffer.
149 */
150 onemore = 1;
151 SET(e);
152 do {
153 if(!done && strm.avail_in < sizeof buf) {
154 if(strm.avail_in)
155 memmove(buf, strm.next_in, strm.avail_in);
156
157 n = Bread(bin, buf+strm.avail_in, sizeof(buf)-strm.avail_in);
158 if(n <= 0)
159 done = 1;
160 else
161 strm.avail_in += n;
162 strm.next_in = buf;
163 }
164 if(strm.avail_out < sizeof obuf) {
165 Bwrite(&bout, obuf, sizeof(obuf)-strm.avail_out);
166 strm.next_out = obuf;
167 strm.avail_out = sizeof obuf;
168 }
169 if(onemore == 0)
170 break;
171 if(strm.avail_in == 0 && strm.avail_out == sizeof obuf)
172 break;
173 } while((e=BZ2_bzDecompress(&strm)) == BZ_OK || onemore--);
174
175 if(e != BZ_STREAM_END) {
176 fprint(2, "bunzip2: decompress failed\n");
177 return 0;
178 }
179
180 if(BZ2_bzDecompressEnd(&strm) != BZ_OK) {
181 fprint(2, "bunzip2: decompress end failed (can't happen)\n");
182 return 0;
183 }
184
185 Bterm(&bout);
186
187 return 1;
188 }
189