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