source: MondoRescue/branches/3.3/mindi-busybox/archival/bbunzip.c@ 3803

Last change on this file since 3803 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 16.8 KB
RevLine 
[1765]1/* vi: set sw=4 ts=4: */
2/*
[2725]3 * Common code for gunzip-like applets
[1765]4 *
[2725]5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[1765]6 */
7#include "libbb.h"
[3232]8#include "bb_archive.h"
[1765]9
[3621]10/* lzop_main() uses bbunpack(), need this: */
11//kbuild:lib-$(CONFIG_LZOP) += bbunzip.o
12
13/* Note: must be kept in sync with archival/lzop.c */
[1765]14enum {
[2725]15 OPT_STDOUT = 1 << 0,
16 OPT_FORCE = 1 << 1,
17 /* only some decompressors: */
18 OPT_VERBOSE = 1 << 2,
[3621]19 OPT_QUIET = 1 << 3,
20 OPT_DECOMPRESS = 1 << 4,
21 OPT_TEST = 1 << 5,
22 SEAMLESS_MAGIC = (1 << 31) * SEAMLESS_COMPRESSION,
[1765]23};
24
25static
26int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
27{
28 int fd = open3_or_warn(filename, flags, mode);
29 if (fd < 0) {
30 return 1;
31 }
32 xmove_fd(fd, to_fd);
33 return 0;
34}
35
[2725]36char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
37{
38 return xasprintf("%s.%s", filename, expected_ext);
39}
40
41int FAST_FUNC bbunpack(char **argv,
[3621]42 IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_state_t *xstate),
[2725]43 char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
44 const char *expected_ext
[1765]45)
46{
47 struct stat stat_buf;
[3621]48 IF_DESKTOP(long long) int status = 0;
[1765]49 char *filename, *new_name;
50 smallint exitcode = 0;
[3621]51 transformer_state_t xstate;
[1765]52
53 do {
54 /* NB: new_name is *maybe* malloc'ed! */
55 new_name = NULL;
56 filename = *argv; /* can be NULL - 'streaming' bunzip2 */
57
58 if (filename && LONE_DASH(filename))
59 filename = NULL;
60
61 /* Open src */
62 if (filename) {
[3621]63 if (!(option_mask32 & SEAMLESS_MAGIC)) {
64 if (stat(filename, &stat_buf) != 0) {
65 err_name:
66 bb_simple_perror_msg(filename);
[1765]67 err:
[3621]68 exitcode = 1;
69 goto free_name;
70 }
71 if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
72 goto err;
73 } else {
74 /* "clever zcat" with FILE */
75 /* fail_if_not_compressed because zcat refuses uncompressed input */
76 int fd = open_zipped(filename, /*fail_if_not_compressed:*/ 1);
77 if (fd < 0)
78 goto err_name;
79 xmove_fd(fd, STDIN_FILENO);
[1765]80 }
[3621]81 } else
82 if (option_mask32 & SEAMLESS_MAGIC) {
83 /* "clever zcat" on stdin */
84 if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_compressed*/ 1))
[1765]85 goto err;
86 }
87
88 /* Special cases: test, stdout */
89 if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
90 if (option_mask32 & OPT_TEST)
91 if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
[3621]92 xfunc_die();
[1765]93 filename = NULL;
94 }
95
96 /* Open dst if we are going to unpack to file */
97 if (filename) {
[2725]98 new_name = make_new_name(filename, expected_ext);
[1765]99 if (!new_name) {
100 bb_error_msg("%s: unknown suffix - ignored", filename);
101 goto err;
102 }
[2725]103
104 /* -f: overwrite existing output files */
105 if (option_mask32 & OPT_FORCE) {
106 unlink(new_name);
107 }
108
[1765]109 /* O_EXCL: "real" bunzip2 doesn't overwrite files */
[2725]110 /* GNU gunzip does not bail out, but goes to next file */
[1765]111 if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
112 stat_buf.st_mode))
113 goto err;
114 }
115
116 /* Check that the input is sane */
[3621]117 if (!(option_mask32 & OPT_FORCE) && isatty(STDIN_FILENO)) {
[1765]118 bb_error_msg_and_die("compressed data not read from terminal, "
119 "use -f to force it");
120 }
121
[3621]122 if (!(option_mask32 & SEAMLESS_MAGIC)) {
123 init_transformer_state(&xstate);
124 xstate.signature_skipped = 0;
125 /*xstate.src_fd = STDIN_FILENO; - already is */
126 xstate.dst_fd = STDOUT_FILENO;
127 status = unpacker(&xstate);
128 if (status < 0)
129 exitcode = 1;
130 } else {
131 if (bb_copyfd_eof(STDIN_FILENO, STDOUT_FILENO) < 0)
132 /* Disk full, tty closed, etc. No point in continuing */
133 xfunc_die();
134 }
[1765]135
[3232]136 if (!(option_mask32 & OPT_STDOUT))
137 xclose(STDOUT_FILENO); /* with error check! */
138
[1765]139 if (filename) {
140 char *del = new_name;
[3621]141
[1765]142 if (status >= 0) {
[3621]143 unsigned new_name_len;
144
[2725]145 /* TODO: restore other things? */
[3621]146 if (xstate.mtime != 0) {
[2725]147 struct timeval times[2];
148
[3621]149 times[1].tv_sec = times[0].tv_sec = xstate.mtime;
[2725]150 times[1].tv_usec = times[0].tv_usec = 0;
151 /* Note: we closed it first.
152 * On some systems calling utimes
153 * then closing resets the mtime
154 * back to current time. */
155 utimes(new_name, times); /* ignoring errors */
156 }
157
[3621]158 if (ENABLE_DESKTOP)
159 new_name_len = strlen(new_name);
160 /* Restore source filename (unless tgz -> tar case) */
161 if (new_name == filename) {
162 new_name_len = strlen(filename);
163 filename[new_name_len] = '.';
164 }
165 /* Extreme bloat for gunzip compat */
166 /* Some users do want this info... */
167 if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE)) {
168 unsigned percent = status
169 ? ((uoff_t)stat_buf.st_size * 100u / (unsigned long long)status)
170 : 0;
171 fprintf(stderr, "%s: %u%% - replaced with %.*s\n",
172 filename,
173 100u - percent,
174 new_name_len, new_name
175 );
176 }
177 /* Delete _source_ file */
[1765]178 del = filename;
179 }
180 xunlink(del);
181 free_name:
182 if (new_name != filename)
183 free(new_name);
184 }
185 } while (*argv && *++argv);
186
[3232]187 if (option_mask32 & OPT_STDOUT)
188 xclose(STDOUT_FILENO); /* with error check! */
189
[1765]190 return exitcode;
191}
192
[2725]193#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
[1765]194static
[2725]195char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
[1765]196{
197 char *extension = strrchr(filename, '.');
198 if (!extension || strcmp(extension + 1, expected_ext) != 0) {
199 /* Mimic GNU gunzip - "real" bunzip2 tries to */
200 /* unpack file anyway, to file.out */
201 return NULL;
202 }
203 *extension = '\0';
204 return filename;
205}
206#endif
207
208
209/*
[2725]210 * Uncompress applet for busybox (c) 2002 Glenn McGrath
[1765]211 *
[2725]212 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[1765]213 */
[3232]214//usage:#define uncompress_trivial_usage
215//usage: "[-cf] [FILE]..."
216//usage:#define uncompress_full_usage "\n\n"
217//usage: "Decompress .Z file[s]\n"
218//usage: "\n -c Write to stdout"
219//usage: "\n -f Overwrite"
220
[3621]221//config:config UNCOMPRESS
222//config: bool "uncompress"
223//config: default n # ancient
224//config: help
225//config: uncompress is used to decompress archives created by compress.
226//config: Not much used anymore, replaced by gzip/gunzip.
227
228//applet:IF_UNCOMPRESS(APPLET(uncompress, BB_DIR_BIN, BB_SUID_DROP))
229//kbuild:lib-$(CONFIG_UNCOMPRESS) += bbunzip.o
[2725]230#if ENABLE_UNCOMPRESS
231int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
232int uncompress_main(int argc UNUSED_PARAM, char **argv)
[1765]233{
234 getopt32(argv, "cf");
235 argv += optind;
236
[3621]237 return bbunpack(argv, unpack_Z_stream, make_new_name_generic, "Z");
[1765]238}
239#endif
240
241
242/*
243 * Gzip implementation for busybox
244 *
245 * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
246 *
247 * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
248 * based on gzip sources
249 *
250 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
251 * well as stdin/stdout, and to generally behave itself wrt command line
252 * handling.
253 *
254 * General cleanup to better adhere to the style guide and make use of standard
[2725]255 * busybox functions by Glenn McGrath
[1765]256 *
[2725]257 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[1765]258 *
259 * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
260 * Copyright (C) 1992-1993 Jean-loup Gailly
261 * The unzip code was written and put in the public domain by Mark Adler.
262 * Portions of the lzw code are derived from the public domain 'compress'
263 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
264 * Ken Turkowski, Dave Mack and Peter Jannesen.
265 */
[3232]266//usage:#define gunzip_trivial_usage
267//usage: "[-cft] [FILE]..."
268//usage:#define gunzip_full_usage "\n\n"
269//usage: "Decompress FILEs (or stdin)\n"
270//usage: "\n -c Write to stdout"
271//usage: "\n -f Force"
272//usage: "\n -t Test file integrity"
273//usage:
274//usage:#define gunzip_example_usage
275//usage: "$ ls -la /tmp/BusyBox*\n"
276//usage: "-rw-rw-r-- 1 andersen andersen 557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n"
277//usage: "$ gunzip /tmp/BusyBox-0.43.tar.gz\n"
278//usage: "$ ls -la /tmp/BusyBox*\n"
279//usage: "-rw-rw-r-- 1 andersen andersen 1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
280//usage:
281//usage:#define zcat_trivial_usage
[3621]282//usage: "[FILE]..."
[3232]283//usage:#define zcat_full_usage "\n\n"
284//usage: "Decompress to stdout"
285
[3621]286//config:config GUNZIP
287//config: bool "gunzip"
288//config: default y
289//config: help
290//config: gunzip is used to decompress archives created by gzip.
291//config: You can use the `-t' option to test the integrity of
292//config: an archive, without decompressing it.
293//config:
294//config:config FEATURE_GUNZIP_LONG_OPTIONS
295//config: bool "Enable long options"
296//config: default y
297//config: depends on GUNZIP && LONG_OPTS
298//config: help
299//config: Enable use of long options.
300
301//applet:IF_GUNZIP(APPLET(gunzip, BB_DIR_BIN, BB_SUID_DROP))
302//applet:IF_GUNZIP(APPLET_ODDNAME(zcat, gunzip, BB_DIR_BIN, BB_SUID_DROP, zcat))
303//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o
304//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o
[1765]305#if ENABLE_GUNZIP
306static
[2725]307char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
[1765]308{
309 char *extension = strrchr(filename, '.');
310
311 if (!extension)
312 return NULL;
313
314 extension++;
315 if (strcmp(extension, "tgz" + 1) == 0
[2725]316#if ENABLE_FEATURE_SEAMLESS_Z
317 || (extension[0] == 'Z' && extension[1] == '\0')
[1765]318#endif
319 ) {
320 extension[-1] = '\0';
321 } else if (strcmp(extension, "tgz") == 0) {
322 filename = xstrdup(filename);
323 extension = strrchr(filename, '.');
324 extension[2] = 'a';
325 extension[3] = 'r';
326 } else {
327 return NULL;
328 }
329 return filename;
330}
[3621]331
332#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS
333static const char gunzip_longopts[] ALIGN1 =
334 "stdout\0" No_argument "c"
335 "to-stdout\0" No_argument "c"
336 "force\0" No_argument "f"
337 "test\0" No_argument "t"
338 "no-name\0" No_argument "n"
339 ;
340#endif
341
[2725]342/*
343 * Linux kernel build uses gzip -d -n. We accept and ignore it.
344 * Man page says:
345 * -n --no-name
346 * gzip: do not save the original file name and time stamp.
347 * (The original name is always saved if the name had to be truncated.)
348 * gunzip: do not restore the original file name/time even if present
349 * (remove only the gzip suffix from the compressed file name).
350 * This option is the default when decompressing.
351 * -N --name
352 * gzip: always save the original file name and time stamp (this is the default)
353 * gunzip: restore the original file name and time stamp if present.
354 */
355int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
356int gunzip_main(int argc UNUSED_PARAM, char **argv)
[1765]357{
[3621]358#if ENABLE_FEATURE_GUNZIP_LONG_OPTIONS
359 applet_long_options = gunzip_longopts;
360#endif
361 getopt32(argv, "cfvqdtn");
[1765]362 argv += optind;
[3621]363
364 /* If called as zcat...
365 * Normally, "zcat" is just "gunzip -c".
366 * But if seamless magic is enabled, then we are much more clever.
367 */
[1765]368 if (applet_name[1] == 'c')
[3621]369 option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
[1765]370
[3621]371 return bbunpack(argv, unpack_gz_stream, make_new_name_gunzip, /*unused:*/ NULL);
[1765]372}
373#endif
374
375
376/*
[2725]377 * Modified for busybox by Glenn McGrath
378 * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
[1765]379 *
[2725]380 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
[1765]381 */
[2725]382//usage:#define bunzip2_trivial_usage
383//usage: "[-cf] [FILE]..."
384//usage:#define bunzip2_full_usage "\n\n"
385//usage: "Decompress FILEs (or stdin)\n"
386//usage: "\n -c Write to stdout"
387//usage: "\n -f Force"
388//usage:#define bzcat_trivial_usage
[3621]389//usage: "[FILE]..."
[2725]390//usage:#define bzcat_full_usage "\n\n"
391//usage: "Decompress to stdout"
[3621]392
393//config:config BUNZIP2
394//config: bool "bunzip2"
395//config: default y
396//config: help
397//config: bunzip2 is a compression utility using the Burrows-Wheeler block
398//config: sorting text compression algorithm, and Huffman coding. Compression
399//config: is generally considerably better than that achieved by more
400//config: conventional LZ77/LZ78-based compressors, and approaches the
401//config: performance of the PPM family of statistical compressors.
402//config:
403//config: Unless you have a specific application which requires bunzip2, you
404//config: should probably say N here.
405
[3232]406//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
407//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
[3621]408//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o
409//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o
[2725]410#if ENABLE_BUNZIP2
411int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
412int bunzip2_main(int argc UNUSED_PARAM, char **argv)
[1765]413{
[3621]414 getopt32(argv, "cfvqdt");
[1765]415 argv += optind;
[2725]416 if (applet_name[2] == 'c') /* bzcat */
[1765]417 option_mask32 |= OPT_STDOUT;
418
[3621]419 return bbunpack(argv, unpack_bz2_stream, make_new_name_generic, "bz2");
[1765]420}
421#endif
422
423
424/*
[2725]425 * Small lzma deflate implementation.
426 * Copyright (C) 2006 Aurelien Jacobs <aurel@gnuage.org>
[1765]427 *
[2725]428 * Based on bunzip.c from busybox
429 *
430 * Licensed under GPLv2, see file LICENSE in this source tree.
[1765]431 */
[3232]432//usage:#define unlzma_trivial_usage
433//usage: "[-cf] [FILE]..."
434//usage:#define unlzma_full_usage "\n\n"
435//usage: "Decompress FILE (or stdin)\n"
436//usage: "\n -c Write to stdout"
437//usage: "\n -f Force"
438//usage:
439//usage:#define lzma_trivial_usage
440//usage: "-d [-cf] [FILE]..."
441//usage:#define lzma_full_usage "\n\n"
442//usage: "Decompress FILE (or stdin)\n"
443//usage: "\n -d Decompress"
444//usage: "\n -c Write to stdout"
445//usage: "\n -f Force"
446//usage:
447//usage:#define lzcat_trivial_usage
[3621]448//usage: "[FILE]..."
[3232]449//usage:#define lzcat_full_usage "\n\n"
450//usage: "Decompress to stdout"
451//usage:
452//usage:#define unxz_trivial_usage
453//usage: "[-cf] [FILE]..."
454//usage:#define unxz_full_usage "\n\n"
455//usage: "Decompress FILE (or stdin)\n"
456//usage: "\n -c Write to stdout"
457//usage: "\n -f Force"
458//usage:
459//usage:#define xz_trivial_usage
460//usage: "-d [-cf] [FILE]..."
461//usage:#define xz_full_usage "\n\n"
462//usage: "Decompress FILE (or stdin)\n"
463//usage: "\n -d Decompress"
464//usage: "\n -c Write to stdout"
465//usage: "\n -f Force"
466//usage:
467//usage:#define xzcat_trivial_usage
[3621]468//usage: "[FILE]..."
[3232]469//usage:#define xzcat_full_usage "\n\n"
470//usage: "Decompress to stdout"
471
[3621]472//config:config UNLZMA
473//config: bool "unlzma"
474//config: default y
475//config: help
476//config: unlzma is a compression utility using the Lempel-Ziv-Markov chain
477//config: compression algorithm, and range coding. Compression
478//config: is generally considerably better than that achieved by the bzip2
479//config: compressors.
480//config:
481//config: The BusyBox unlzma applet is limited to decompression only.
482//config: On an x86 system, this applet adds about 4K.
483//config:
484//config:config FEATURE_LZMA_FAST
485//config: bool "Optimize unlzma for speed"
486//config: default n
487//config: depends on UNLZMA
488//config: help
489//config: This option reduces decompression time by about 25% at the cost of
490//config: a 1K bigger binary.
491//config:
492//config:config LZMA
493//config: bool "Provide lzma alias which supports only unpacking"
494//config: default y
495//config: depends on UNLZMA
496//config: help
497//config: Enable this option if you want commands like "lzma -d" to work.
498//config: IOW: you'll get lzma applet, but it will always require -d option.
499
500//applet:IF_UNLZMA(APPLET(unlzma, BB_DIR_USR_BIN, BB_SUID_DROP))
501//applet:IF_UNLZMA(APPLET_ODDNAME(lzcat, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzcat))
502//applet:IF_LZMA(APPLET_ODDNAME(lzma, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzma))
503//kbuild:lib-$(CONFIG_UNLZMA) += bbunzip.o
[2725]504#if ENABLE_UNLZMA
505int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
506int unlzma_main(int argc UNUSED_PARAM, char **argv)
507{
[3621]508 IF_LZMA(int opts =) getopt32(argv, "cfvqdt");
[2725]509# if ENABLE_LZMA
510 /* lzma without -d or -t? */
511 if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
512 bb_show_usage();
513# endif
514 /* lzcat? */
515 if (applet_name[2] == 'c')
516 option_mask32 |= OPT_STDOUT;
[1765]517
[2725]518 argv += optind;
[3621]519 return bbunpack(argv, unpack_lzma_stream, make_new_name_generic, "lzma");
[2725]520}
521#endif
522
523
[3621]524//config:config UNXZ
525//config: bool "unxz"
526//config: default y
527//config: help
528//config: unxz is a unlzma successor.
529//config:
530//config:config XZ
531//config: bool "Provide xz alias which supports only unpacking"
532//config: default y
533//config: depends on UNXZ
534//config: help
535//config: Enable this option if you want commands like "xz -d" to work.
536//config: IOW: you'll get xz applet, but it will always require -d option.
537
538//applet:IF_UNXZ(APPLET(unxz, BB_DIR_USR_BIN, BB_SUID_DROP))
539//applet:IF_UNXZ(APPLET_ODDNAME(xzcat, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xzcat))
540//applet:IF_XZ(APPLET_ODDNAME(xz, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xz))
541//kbuild:lib-$(CONFIG_UNXZ) += bbunzip.o
[2725]542#if ENABLE_UNXZ
543int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
544int unxz_main(int argc UNUSED_PARAM, char **argv)
545{
[3621]546 IF_XZ(int opts =) getopt32(argv, "cfvqdt");
[2725]547# if ENABLE_XZ
548 /* xz without -d or -t? */
549 if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
550 bb_show_usage();
551# endif
552 /* xzcat? */
553 if (applet_name[2] == 'c')
554 option_mask32 |= OPT_STDOUT;
[1765]555
556 argv += optind;
[3621]557 return bbunpack(argv, unpack_xz_stream, make_new_name_generic, "xz");
[1765]558}
559#endif
Note: See TracBrowser for help on using the repository browser.