1
2
3
4
5
6
7#include "libbb.h"
8#include "archive.h"
9
10enum {
11 OPT_STDOUT = 1 << 0,
12 OPT_FORCE = 1 << 1,
13
14 OPT_VERBOSE = 1 << 2,
15 OPT_DECOMPRESS = 1 << 3,
16 OPT_TEST = 1 << 4,
17};
18
19static
20int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
21{
22 int fd = open3_or_warn(filename, flags, mode);
23 if (fd < 0) {
24 return 1;
25 }
26 xmove_fd(fd, to_fd);
27 return 0;
28}
29
30char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
31{
32 return xasprintf("%s.%s", filename, expected_ext);
33}
34
35int FAST_FUNC bbunpack(char **argv,
36 IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(unpack_info_t *info),
37 char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
38 const char *expected_ext
39)
40{
41 struct stat stat_buf;
42 IF_DESKTOP(long long) int status;
43 char *filename, *new_name;
44 smallint exitcode = 0;
45 unpack_info_t info;
46
47 do {
48
49 new_name = NULL;
50 filename = *argv;
51
52 if (filename && LONE_DASH(filename))
53 filename = NULL;
54
55
56 if (filename) {
57 if (stat(filename, &stat_buf) != 0) {
58 bb_simple_perror_msg(filename);
59 err:
60 exitcode = 1;
61 goto free_name;
62 }
63 if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
64 goto err;
65 }
66
67
68 if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
69 if (option_mask32 & OPT_TEST)
70 if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
71 goto err;
72 filename = NULL;
73 }
74
75
76 if (filename) {
77 new_name = make_new_name(filename, expected_ext);
78 if (!new_name) {
79 bb_error_msg("%s: unknown suffix - ignored", filename);
80 goto err;
81 }
82
83
84 if (option_mask32 & OPT_FORCE) {
85 unlink(new_name);
86 }
87
88
89
90 if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
91 stat_buf.st_mode))
92 goto err;
93 }
94
95
96 if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) {
97 bb_error_msg_and_die("compressed data not read from terminal, "
98 "use -f to force it");
99 }
100
101
102 info.mtime = 0;
103 status = unpacker(&info);
104 if (status < 0)
105 exitcode = 1;
106 xclose(STDOUT_FILENO);
107
108 if (filename) {
109 char *del = new_name;
110 if (status >= 0) {
111
112 if (info.mtime) {
113 struct timeval times[2];
114
115 times[1].tv_sec = times[0].tv_sec = info.mtime;
116 times[1].tv_usec = times[0].tv_usec = 0;
117
118
119
120
121 utimes(new_name, times);
122 }
123
124
125 del = filename;
126
127 if (new_name == filename)
128 filename[strlen(filename)] = '.';
129 }
130 xunlink(del);
131
132#if 0
133
134 if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
135 fprintf(stderr, "%s: %u%% - replaced with %s\n",
136 filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
137 }
138#endif
139
140 free_name:
141 if (new_name != filename)
142 free(new_name);
143 }
144 } while (*argv && *++argv);
145
146 return exitcode;
147}
148
149#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
150static
151char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
152{
153 char *extension = strrchr(filename, '.');
154 if (!extension || strcmp(extension + 1, expected_ext) != 0) {
155
156
157 return NULL;
158 }
159 *extension = '\0';
160 return filename;
161}
162#endif
163
164
165
166
167
168
169
170#if ENABLE_UNCOMPRESS
171static
172IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(unpack_info_t *info UNUSED_PARAM)
173{
174 IF_DESKTOP(long long) int status = -1;
175
176 if ((xread_char(STDIN_FILENO) != 0x1f) || (xread_char(STDIN_FILENO) != 0x9d)) {
177 bb_error_msg("invalid magic");
178 } else {
179 status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
180 }
181 return status;
182}
183int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
184int uncompress_main(int argc UNUSED_PARAM, char **argv)
185{
186 getopt32(argv, "cf");
187 argv += optind;
188
189 return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
190}
191#endif
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221#if ENABLE_GUNZIP
222static
223char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
224{
225 char *extension = strrchr(filename, '.');
226
227 if (!extension)
228 return NULL;
229
230 extension++;
231 if (strcmp(extension, "tgz" + 1) == 0
232#if ENABLE_FEATURE_SEAMLESS_Z
233 || (extension[0] == 'Z' && extension[1] == '\0')
234#endif
235 ) {
236 extension[-1] = '\0';
237 } else if (strcmp(extension, "tgz") == 0) {
238 filename = xstrdup(filename);
239 extension = strrchr(filename, '.');
240 extension[2] = 'a';
241 extension[3] = 'r';
242 } else {
243 return NULL;
244 }
245 return filename;
246}
247static
248IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(unpack_info_t *info)
249{
250 IF_DESKTOP(long long) int status = -1;
251
252
253 if (xread_char(STDIN_FILENO) == 0x1f) {
254 unsigned char magic2;
255
256 magic2 = xread_char(STDIN_FILENO);
257 if (ENABLE_FEATURE_SEAMLESS_Z && magic2 == 0x9d) {
258 status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
259 } else if (magic2 == 0x8b) {
260 status = unpack_gz_stream_with_info(STDIN_FILENO, STDOUT_FILENO, info);
261 } else {
262 goto bad_magic;
263 }
264 if (status < 0) {
265 bb_error_msg("error inflating");
266 }
267 } else {
268 bad_magic:
269 bb_error_msg("invalid magic");
270
271 }
272 return status;
273}
274
275
276
277
278
279
280
281
282
283
284
285
286
287int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
288int gunzip_main(int argc UNUSED_PARAM, char **argv)
289{
290 getopt32(argv, "cfvdtn");
291 argv += optind;
292
293 if (applet_name[1] == 'c')
294 option_mask32 |= OPT_STDOUT;
295
296 return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, NULL);
297}
298#endif
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320#if ENABLE_BUNZIP2
321static
322IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(unpack_info_t *info UNUSED_PARAM)
323{
324 return unpack_bz2_stream_prime(STDIN_FILENO, STDOUT_FILENO);
325}
326int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
327int bunzip2_main(int argc UNUSED_PARAM, char **argv)
328{
329 getopt32(argv, "cfvdt");
330 argv += optind;
331 if (applet_name[2] == 'c')
332 option_mask32 |= OPT_STDOUT;
333
334 return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
335}
336#endif
337
338
339
340
341
342
343
344
345
346
347#if ENABLE_UNLZMA
348static
349IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(unpack_info_t *info UNUSED_PARAM)
350{
351 return unpack_lzma_stream(STDIN_FILENO, STDOUT_FILENO);
352}
353int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
354int unlzma_main(int argc UNUSED_PARAM, char **argv)
355{
356 IF_LZMA(int opts =) getopt32(argv, "cfvdt");
357# if ENABLE_LZMA
358
359 if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
360 bb_show_usage();
361# endif
362
363 if (applet_name[2] == 'c')
364 option_mask32 |= OPT_STDOUT;
365
366 argv += optind;
367 return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
368}
369#endif
370
371
372#if ENABLE_UNXZ
373static
374IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(unpack_info_t *info UNUSED_PARAM)
375{
376 struct {
377 uint32_t v1;
378 uint16_t v2;
379 } magic;
380 xread(STDIN_FILENO, &magic, 6);
381 if (magic.v1 != XZ_MAGIC1a || magic.v2 != XZ_MAGIC2a) {
382 bb_error_msg("invalid magic");
383 return -1;
384 }
385 return unpack_xz_stream(STDIN_FILENO, STDOUT_FILENO);
386}
387int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
388int unxz_main(int argc UNUSED_PARAM, char **argv)
389{
390 IF_XZ(int opts =) getopt32(argv, "cfvdt");
391# if ENABLE_XZ
392
393 if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
394 bb_show_usage();
395# endif
396
397 if (applet_name[2] == 'c')
398 option_mask32 |= OPT_STDOUT;
399
400 argv += optind;
401 return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
402}
403#endif
404