bbunzip: fix order of flags vs bit defines
[oweals/busybox.git] / archival / bbunzip.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Common code for gunzip-like applets
4  *
5  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6  */
7 #include "libbb.h"
8 #include "bb_archive.h"
9
10 enum {
11         OPT_STDOUT     = 1 << 0,
12         OPT_FORCE      = 1 << 1,
13         /* only some decompressors: */
14         OPT_VERBOSE    = 1 << 2,
15         OPT_QUIET      = 1 << 3,
16         OPT_DECOMPRESS = 1 << 4,
17         OPT_TEST       = 1 << 5,
18         SEAMLESS_MAGIC = (1 << 31) * SEAMLESS_COMPRESSION,
19 };
20
21 static
22 int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
23 {
24         int fd = open3_or_warn(filename, flags, mode);
25         if (fd < 0) {
26                 return 1;
27         }
28         xmove_fd(fd, to_fd);
29         return 0;
30 }
31
32 char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
33 {
34         return xasprintf("%s.%s", filename, expected_ext);
35 }
36
37 int FAST_FUNC bbunpack(char **argv,
38         IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
39         char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
40         const char *expected_ext
41 )
42 {
43         struct stat stat_buf;
44         IF_DESKTOP(long long) int status = 0;
45         char *filename, *new_name;
46         smallint exitcode = 0;
47         transformer_aux_data_t aux;
48
49         do {
50                 /* NB: new_name is *maybe* malloc'ed! */
51                 new_name = NULL;
52                 filename = *argv; /* can be NULL - 'streaming' bunzip2 */
53
54                 if (filename && LONE_DASH(filename))
55                         filename = NULL;
56
57                 /* Open src */
58                 if (filename) {
59                         if (!(option_mask32 & SEAMLESS_MAGIC)) {
60                                 if (stat(filename, &stat_buf) != 0) {
61  err_name:
62                                         bb_simple_perror_msg(filename);
63  err:
64                                         exitcode = 1;
65                                         goto free_name;
66                                 }
67                                 if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
68                                         goto err;
69                         } else {
70                                 /* "clever zcat" with FILE */
71                                 int fd = open_zipped(filename);
72                                 if (fd < 0)
73                                         goto err_name;
74                                 xmove_fd(fd, STDIN_FILENO);
75                         }
76                 } else
77                 if (option_mask32 & SEAMLESS_MAGIC) {
78                         /* "clever zcat" on stdin */
79                         if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_detected*/ 0))
80                                 goto err;
81                 }
82
83                 /* Special cases: test, stdout */
84                 if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
85                         if (option_mask32 & OPT_TEST)
86                                 if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
87                                         xfunc_die();
88                         filename = NULL;
89                 }
90
91                 /* Open dst if we are going to unpack to file */
92                 if (filename) {
93                         new_name = make_new_name(filename, expected_ext);
94                         if (!new_name) {
95                                 bb_error_msg("%s: unknown suffix - ignored", filename);
96                                 goto err;
97                         }
98
99                         /* -f: overwrite existing output files */
100                         if (option_mask32 & OPT_FORCE) {
101                                 unlink(new_name);
102                         }
103
104                         /* O_EXCL: "real" bunzip2 doesn't overwrite files */
105                         /* GNU gunzip does not bail out, but goes to next file */
106                         if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
107                                         stat_buf.st_mode))
108                                 goto err;
109                 }
110
111                 /* Check that the input is sane */
112                 if (!(option_mask32 & OPT_FORCE) && isatty(STDIN_FILENO)) {
113                         bb_error_msg_and_die("compressed data not read from terminal, "
114                                         "use -f to force it");
115                 }
116
117                 if (!(option_mask32 & SEAMLESS_MAGIC)) {
118                         init_transformer_aux_data(&aux);
119                         aux.check_signature = 1;
120                         status = unpacker(&aux);
121                         if (status < 0)
122                                 exitcode = 1;
123                 } else {
124                         if (bb_copyfd_eof(STDIN_FILENO, STDOUT_FILENO) < 0)
125                                 /* Disk full, tty closed, etc. No point in continuing */
126                                 xfunc_die();
127                 }
128
129                 if (!(option_mask32 & OPT_STDOUT))
130                         xclose(STDOUT_FILENO); /* with error check! */
131
132                 if (filename) {
133                         char *del = new_name;
134                         if (status >= 0) {
135                                 /* TODO: restore other things? */
136                                 if (aux.mtime != 0) {
137                                         struct timeval times[2];
138
139                                         times[1].tv_sec = times[0].tv_sec = aux.mtime;
140                                         times[1].tv_usec = times[0].tv_usec = 0;
141                                         /* Note: we closed it first.
142                                          * On some systems calling utimes
143                                          * then closing resets the mtime
144                                          * back to current time. */
145                                         utimes(new_name, times); /* ignoring errors */
146                                 }
147
148                                 /* Delete _compressed_ file */
149                                 del = filename;
150                                 /* restore extension (unless tgz -> tar case) */
151                                 if (new_name == filename)
152                                         filename[strlen(filename)] = '.';
153                         }
154                         xunlink(del);
155
156 #if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
157                         /* Extreme bloat for gunzip compat */
158                         if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
159                                 fprintf(stderr, "%s: %u%% - replaced with %s\n",
160                                         filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
161                         }
162 #endif
163
164  free_name:
165                         if (new_name != filename)
166                                 free(new_name);
167                 }
168         } while (*argv && *++argv);
169
170         if (option_mask32 & OPT_STDOUT)
171                 xclose(STDOUT_FILENO); /* with error check! */
172
173         return exitcode;
174 }
175
176 #if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
177 static
178 char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
179 {
180         char *extension = strrchr(filename, '.');
181         if (!extension || strcmp(extension + 1, expected_ext) != 0) {
182                 /* Mimic GNU gunzip - "real" bunzip2 tries to */
183                 /* unpack file anyway, to file.out */
184                 return NULL;
185         }
186         *extension = '\0';
187         return filename;
188 }
189 #endif
190
191
192 /*
193  * Uncompress applet for busybox (c) 2002 Glenn McGrath
194  *
195  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
196  */
197
198 //usage:#define uncompress_trivial_usage
199 //usage:       "[-cf] [FILE]..."
200 //usage:#define uncompress_full_usage "\n\n"
201 //usage:       "Decompress .Z file[s]\n"
202 //usage:     "\n        -c      Write to stdout"
203 //usage:     "\n        -f      Overwrite"
204
205 #if ENABLE_UNCOMPRESS
206 static
207 IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(transformer_aux_data_t *aux)
208 {
209         return unpack_Z_stream(aux, STDIN_FILENO, STDOUT_FILENO);
210 }
211 int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
212 int uncompress_main(int argc UNUSED_PARAM, char **argv)
213 {
214         getopt32(argv, "cf");
215         argv += optind;
216
217         return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
218 }
219 #endif
220
221
222 /*
223  * Gzip implementation for busybox
224  *
225  * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
226  *
227  * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
228  * based on gzip sources
229  *
230  * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
231  * well as stdin/stdout, and to generally behave itself wrt command line
232  * handling.
233  *
234  * General cleanup to better adhere to the style guide and make use of standard
235  * busybox functions by Glenn McGrath
236  *
237  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
238  *
239  * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
240  * Copyright (C) 1992-1993 Jean-loup Gailly
241  * The unzip code was written and put in the public domain by Mark Adler.
242  * Portions of the lzw code are derived from the public domain 'compress'
243  * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
244  * Ken Turkowski, Dave Mack and Peter Jannesen.
245  *
246  * See the license_msg below and the file COPYING for the software license.
247  * See the file algorithm.doc for the compression algorithms and file formats.
248  */
249
250 //usage:#define gunzip_trivial_usage
251 //usage:       "[-cft] [FILE]..."
252 //usage:#define gunzip_full_usage "\n\n"
253 //usage:       "Decompress FILEs (or stdin)\n"
254 //usage:     "\n        -c      Write to stdout"
255 //usage:     "\n        -f      Force"
256 //usage:     "\n        -t      Test file integrity"
257 //usage:
258 //usage:#define gunzip_example_usage
259 //usage:       "$ ls -la /tmp/BusyBox*\n"
260 //usage:       "-rw-rw-r--    1 andersen andersen   557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n"
261 //usage:       "$ gunzip /tmp/BusyBox-0.43.tar.gz\n"
262 //usage:       "$ ls -la /tmp/BusyBox*\n"
263 //usage:       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
264 //usage:
265 //usage:#define zcat_trivial_usage
266 //usage:       "[FILE]..."
267 //usage:#define zcat_full_usage "\n\n"
268 //usage:       "Decompress to stdout"
269
270 #if ENABLE_GUNZIP
271 static
272 char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
273 {
274         char *extension = strrchr(filename, '.');
275
276         if (!extension)
277                 return NULL;
278
279         extension++;
280         if (strcmp(extension, "tgz" + 1) == 0
281 #if ENABLE_FEATURE_SEAMLESS_Z
282          || (extension[0] == 'Z' && extension[1] == '\0')
283 #endif
284         ) {
285                 extension[-1] = '\0';
286         } else if (strcmp(extension, "tgz") == 0) {
287                 filename = xstrdup(filename);
288                 extension = strrchr(filename, '.');
289                 extension[2] = 'a';
290                 extension[3] = 'r';
291         } else {
292                 return NULL;
293         }
294         return filename;
295 }
296 static
297 IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
298 {
299         return unpack_gz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
300 }
301 /*
302  * Linux kernel build uses gzip -d -n. We accept and ignore it.
303  * Man page says:
304  * -n --no-name
305  * gzip: do not save the original file name and time stamp.
306  * (The original name is always saved if the name had to be truncated.)
307  * gunzip: do not restore the original file name/time even if present
308  * (remove only the gzip suffix from the compressed file name).
309  * This option is the default when decompressing.
310  * -N --name
311  * gzip: always save the original file name and time stamp (this is the default)
312  * gunzip: restore the original file name and time stamp if present.
313  */
314 int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
315 int gunzip_main(int argc UNUSED_PARAM, char **argv)
316 {
317         getopt32(argv, "cfvqdtn");
318         argv += optind;
319
320         /* If called as zcat...
321          * Normally, "zcat" is just "gunzip -c".
322          * But if seamless magic is enabled, then we are much more clever.
323          */
324         if (applet_name[1] == 'c')
325                 option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
326
327         return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
328 }
329 #endif
330
331
332 /*
333  * Modified for busybox by Glenn McGrath
334  * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
335  *
336  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
337  */
338 //usage:#define bunzip2_trivial_usage
339 //usage:       "[-cf] [FILE]..."
340 //usage:#define bunzip2_full_usage "\n\n"
341 //usage:       "Decompress FILEs (or stdin)\n"
342 //usage:     "\n        -c      Write to stdout"
343 //usage:     "\n        -f      Force"
344 //usage:#define bzcat_trivial_usage
345 //usage:       "[FILE]..."
346 //usage:#define bzcat_full_usage "\n\n"
347 //usage:       "Decompress to stdout"
348 //applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
349 //applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
350 #if ENABLE_BUNZIP2
351 static
352 IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(transformer_aux_data_t *aux)
353 {
354         return unpack_bz2_stream(aux, STDIN_FILENO, STDOUT_FILENO);
355 }
356 int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
357 int bunzip2_main(int argc UNUSED_PARAM, char **argv)
358 {
359         getopt32(argv, "cfvqdt");
360         argv += optind;
361         if (applet_name[2] == 'c') /* bzcat */
362                 option_mask32 |= OPT_STDOUT;
363
364         return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
365 }
366 #endif
367
368
369 /*
370  * Small lzma deflate implementation.
371  * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
372  *
373  * Based on bunzip.c from busybox
374  *
375  * Licensed under GPLv2, see file LICENSE in this source tree.
376  */
377
378 //usage:#define unlzma_trivial_usage
379 //usage:       "[-cf] [FILE]..."
380 //usage:#define unlzma_full_usage "\n\n"
381 //usage:       "Decompress FILE (or stdin)\n"
382 //usage:     "\n        -c      Write to stdout"
383 //usage:     "\n        -f      Force"
384 //usage:
385 //usage:#define lzma_trivial_usage
386 //usage:       "-d [-cf] [FILE]..."
387 //usage:#define lzma_full_usage "\n\n"
388 //usage:       "Decompress FILE (or stdin)\n"
389 //usage:     "\n        -d      Decompress"
390 //usage:     "\n        -c      Write to stdout"
391 //usage:     "\n        -f      Force"
392 //usage:
393 //usage:#define lzcat_trivial_usage
394 //usage:       "[FILE]..."
395 //usage:#define lzcat_full_usage "\n\n"
396 //usage:       "Decompress to stdout"
397 //usage:
398 //usage:#define unxz_trivial_usage
399 //usage:       "[-cf] [FILE]..."
400 //usage:#define unxz_full_usage "\n\n"
401 //usage:       "Decompress FILE (or stdin)\n"
402 //usage:     "\n        -c      Write to stdout"
403 //usage:     "\n        -f      Force"
404 //usage:
405 //usage:#define xz_trivial_usage
406 //usage:       "-d [-cf] [FILE]..."
407 //usage:#define xz_full_usage "\n\n"
408 //usage:       "Decompress FILE (or stdin)\n"
409 //usage:     "\n        -d      Decompress"
410 //usage:     "\n        -c      Write to stdout"
411 //usage:     "\n        -f      Force"
412 //usage:
413 //usage:#define xzcat_trivial_usage
414 //usage:       "[FILE]..."
415 //usage:#define xzcat_full_usage "\n\n"
416 //usage:       "Decompress to stdout"
417
418 #if ENABLE_UNLZMA
419 static
420 IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(transformer_aux_data_t *aux)
421 {
422         return unpack_lzma_stream(aux, STDIN_FILENO, STDOUT_FILENO);
423 }
424 int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
425 int unlzma_main(int argc UNUSED_PARAM, char **argv)
426 {
427         IF_LZMA(int opts =) getopt32(argv, "cfvqdt");
428 # if ENABLE_LZMA
429         /* lzma without -d or -t? */
430         if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
431                 bb_show_usage();
432 # endif
433         /* lzcat? */
434         if (applet_name[2] == 'c')
435                 option_mask32 |= OPT_STDOUT;
436
437         argv += optind;
438         return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
439 }
440 #endif
441
442
443 #if ENABLE_UNXZ
444 static
445 IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(transformer_aux_data_t *aux)
446 {
447         return unpack_xz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
448 }
449 int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
450 int unxz_main(int argc UNUSED_PARAM, char **argv)
451 {
452         IF_XZ(int opts =) getopt32(argv, "cfvqdt");
453 # if ENABLE_XZ
454         /* xz without -d or -t? */
455         if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
456                 bb_show_usage();
457 # endif
458         /* xzcat? */
459         if (applet_name[2] == 'c')
460                 option_mask32 |= OPT_STDOUT;
461
462         argv += optind;
463         return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
464 }
465 #endif