typo fix in a comment in a testcase. oh well...
[oweals/busybox.git] / archival / bbunzip.c
index 7ec8f4f0ab860b9bc2ed5fb0aafc701a861c789c..d25f5093947dcf307b90a38420005fd90c80a2ba 100644 (file)
@@ -1,62 +1,56 @@
 /* vi: set sw=4 ts=4: */
 /*
- *  Modified for busybox by Glenn McGrath <bug1@iinet.net.au>
- *  Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
+ *  Common code for gunzip-like applets
  *
  *  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  */
 
-#include "busybox.h"
+#include "libbb.h"
 #include "unarchive.h"
 
 enum {
-       OPT_STDOUT = 1,
-       OPT_FORCE = 2,
-/* gunzip only: */
-       OPT_TEST = 4,
-       OPT_DECOMPRESS = 8,
-       OPT_VERBOSE = 0x10,
+       OPT_STDOUT = 0x1,
+       OPT_FORCE = 0x2,
+/* gunzip and bunzip2 only: */
+       OPT_VERBOSE = 0x4,
+       OPT_DECOMPRESS = 0x8,
+       OPT_TEST = 0x10,
 };
 
 static
 int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
 {
-       int fd = open(filename, flags, mode);
+       int fd = open3_or_warn(filename, flags, mode);
        if (fd < 0) {
-               bb_perror_msg("%s", filename);
                return 1;
        }
-       if (fd != to_fd) {
-               if (dup2(fd, to_fd) < 0)
-                       bb_perror_msg_and_die("cannot dup");
-               close(fd);
-       }
+       xmove_fd(fd, to_fd);
        return 0;
 }
 
-static
-int unpack(char **argv,
+int FAST_FUNC bbunpack(char **argv,
        char* (*make_new_name)(char *filename),
-       USE_DESKTOP(long long) int (*unpacker)(void)
+       IF_DESKTOP(long long) int (*unpacker)(unpack_info_t *info)
 )
 {
        struct stat stat_buf;
-       USE_DESKTOP(long long) int status;
-       char *filename;
-       /* NB: new_name is *possibly* malloc'ed! */
+       IF_DESKTOP(long long) int status;
+       char *filename, *new_name;
        smallint exitcode = 0;
+       unpack_info_t info;
 
        do {
-               char *new_name = NULL;
-
+               /* NB: new_name is *maybe* malloc'ed! */
+               new_name = NULL;
                filename = *argv; /* can be NULL - 'streaming' bunzip2 */
+
                if (filename && LONE_DASH(filename))
                        filename = NULL;
 
                /* Open src */
                if (filename) {
                        if (stat(filename, &stat_buf) != 0) {
-                               bb_perror_msg("%s", filename);
+                               bb_simple_perror_msg(filename);
  err:
                                exitcode = 1;
                                goto free_name;
@@ -73,43 +67,63 @@ int unpack(char **argv,
                        filename = NULL;
                }
 
-               /* Open dst unless -c, "-" or called as bzcat */
+               /* Open dst if we are going to unpack to file */
                if (filename) {
                        new_name = make_new_name(filename);
                        if (!new_name) {
                                bb_error_msg("%s: unknown suffix - ignored", filename);
                                goto err;
                        }
-                       /* O_EXCL: "real" bunzip2 doesn't overwrite files too */
-                       /* TODO: "real" gunzip goes not bail out, but goes
-                        * to next file */
+
+                       /* -f: overwrite existing output files */
+                       if (option_mask32 & OPT_FORCE) {
+                               unlink(new_name);
+                       }
+
+                       /* O_EXCL: "real" bunzip2 doesn't overwrite files */
+                       /* GNU gunzip does not bail out, but goes to next file */
                        if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
                                        stat_buf.st_mode))
                                goto err;
                }
 
-               /* Check that the input is sane. */
+               /* Check that the input is sane */
                if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) {
                        bb_error_msg_and_die("compressed data not read from terminal, "
                                        "use -f to force it");
                }
 
-               status = unpacker();
+               /* memset(&info, 0, sizeof(info)); */
+               info.mtime = 0; /* so far it has one member only */
+               status = unpacker(&info);
                if (status < 0)
                        exitcode = 1;
 
                if (filename) {
                        char *del = new_name;
                        if (status >= 0) {
-                               /* TODO: restore user/group/times here? */
-                               /* delete _old_ file */
+                               /* TODO: restore other things? */
+                               if (info.mtime) {
+                                       struct utimbuf times;
+
+                                       times.actime = info.mtime;
+                                       times.modtime = info.mtime;
+                                       /* Close first.
+                                        * On some systems calling utime
+                                        * then closing resets the mtime. */
+                                       close(STDOUT_FILENO);
+                                       /* Ignoring errors */
+                                       utime(new_name, &times);
+                               }
+
+                               /* Delete _compressed_ file */
                                del = filename;
-                               /* Restore extension (unless tgz -> tar case) */
+                               /* restore extension (unless tgz -> tar case) */
                                if (new_name == filename)
                                        filename[strlen(filename)] = '.';
                        }
-                       if (unlink(del) < 0)
-                               bb_perror_msg_and_die("cannot remove %s", del);
+                       xunlink(del);
+
 #if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
                        /* Extreme bloat for gunzip compat */
                        if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
@@ -117,6 +131,7 @@ int unpack(char **argv,
                                        filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
                        }
 #endif
+
  free_name:
                        if (new_name != filename)
                                free(new_name);
@@ -126,36 +141,54 @@ int unpack(char **argv,
        return exitcode;
 }
 
-#if ENABLE_BUNZIP2
+#if ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNCOMPRESS
 
 static
-char* make_new_name_bunzip2(char *filename)
+char* make_new_name_generic(char *filename, const char *expected_ext)
 {
        char *extension = strrchr(filename, '.');
-       if (!extension || strcmp(extension, ".bz2") != 0) {
-               /* Mimic GNU gunzip ("real" bunzip2 tries to */
-               /* unpack file anyway, to file.out) */
+       if (!extension || strcmp(extension + 1, expected_ext) != 0) {
+               /* Mimic GNU gunzip "real" bunzip2 tries to */
+               /* unpack file anyway, to file.out */
                return NULL;
        }
        *extension = '\0';
        return filename;
 }
 
+#endif
+
+
+/*
+ *  Modified for busybox by Glenn McGrath
+ *  Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
+ *
+ *  Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#if ENABLE_BUNZIP2
+
+static
+char* make_new_name_bunzip2(char *filename)
+{
+       return make_new_name_generic(filename, "bz2");
+}
+
 static
-USE_DESKTOP(long long) int unpack_bunzip2(void)
+IF_DESKTOP(long long) int unpack_bunzip2(unpack_info_t *info UNUSED_PARAM)
 {
-       return uncompressStream(STDIN_FILENO, STDOUT_FILENO);
+       return unpack_bz2_stream_prime(STDIN_FILENO, STDOUT_FILENO);
 }
 
-int bunzip2_main(int argc, char **argv);
-int bunzip2_main(int argc, char **argv)
+int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int bunzip2_main(int argc UNUSED_PARAM, char **argv)
 {
-       getopt32(argc, argv, "cf");
+       getopt32(argv, "cfvdt");
        argv += optind;
        if (applet_name[2] == 'c')
                option_mask32 |= OPT_STDOUT;
 
-       return unpack(argv, make_new_name_bunzip2, unpack_bunzip2);
+       return bbunpack(argv, make_new_name_bunzip2, unpack_bunzip2);
 }
 
 #endif
@@ -174,7 +207,7 @@ int bunzip2_main(int argc, char **argv)
  * handling.
  *
  * General cleanup to better adhere to the style guide and make use of standard
- * busybox functions by Glenn McGrath <bug1@iinet.net.au>
+ * busybox functions by Glenn McGrath
  *
  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  *
@@ -199,13 +232,14 @@ char* make_new_name_gunzip(char *filename)
        if (!extension)
                return NULL;
 
-       if (strcmp(extension, ".gz") == 0
-#ifdef CONFIG_FEATURE_GUNZIP_UNCOMPRESS
-        || strcmp(extension, ".Z") == 0
+       extension++;
+       if (strcmp(extension, "tgz" + 1) == 0
+#if ENABLE_FEATURE_SEAMLESS_Z
+        || (extension[0] == 'Z' && extension[1] == '\0')
 #endif
        ) {
-               *extension = '\0';
-       } else if(strcmp(extension, ".tgz") == 0) {
+               extension[-1] = '\0';
+       } else if (strcmp(extension, "tgz") == 0) {
                filename = xstrdup(filename);
                extension = strrchr(filename, '.');
                extension[2] = 'a';
@@ -217,20 +251,19 @@ char* make_new_name_gunzip(char *filename)
 }
 
 static
-USE_DESKTOP(long long) int unpack_gunzip(void)
+IF_DESKTOP(long long) int unpack_gunzip(unpack_info_t *info)
 {
-       USE_DESKTOP(long long) int status = -1;
+       IF_DESKTOP(long long) int status = -1;
 
        /* do the decompression, and cleanup */
        if (xread_char(STDIN_FILENO) == 0x1f) {
                unsigned char magic2;
 
                magic2 = xread_char(STDIN_FILENO);
-               if (ENABLE_FEATURE_GUNZIP_UNCOMPRESS && magic2 == 0x9d) {
-                       status = uncompress(STDIN_FILENO, STDOUT_FILENO);
+               if (ENABLE_FEATURE_SEAMLESS_Z && magic2 == 0x9d) {
+                       status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
                } else if (magic2 == 0x8b) {
-                       check_header_gzip_or_die(STDIN_FILENO);
-                       status = inflate_gunzip(STDIN_FILENO, STDOUT_FILENO);
+                       status = unpack_gz_stream_with_info(STDIN_FILENO, STDOUT_FILENO, info);
                } else {
                        goto bad_magic;
                }
@@ -245,16 +278,30 @@ USE_DESKTOP(long long) int unpack_gunzip(void)
        return status;
 }
 
-int gunzip_main(int argc, char **argv);
-int gunzip_main(int argc, char **argv)
+/*
+ * Linux kernel build uses gzip -d -n. We accept and ignore it.
+ * Man page says:
+ * -n --no-name
+ * gzip: do not save the original file name and time stamp.
+ * (The original name is always saved if the name had to be truncated.)
+ * gunzip: do not restore the original file name/time even if present
+ * (remove only the gzip suffix from the compressed file name).
+ * This option is the default when decompressing.
+ * -N --name
+ * gzip: always save the original file name and time stamp (this is the default)
+ * gunzip: restore the original file name and time stamp if present.
+ */
+
+int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int gunzip_main(int argc UNUSED_PARAM, char **argv)
 {
-       getopt32(argc, argv, "cftdv");
+       getopt32(argv, "cfvdtn");
        argv += optind;
        /* if called as zcat */
        if (applet_name[1] == 'c')
                option_mask32 |= OPT_STDOUT;
 
-       return unpack(argv, make_new_name_gunzip, unpack_gunzip);
+       return bbunpack(argv, make_new_name_gunzip, unpack_gunzip);
 }
 
 #endif
@@ -274,35 +321,30 @@ int gunzip_main(int argc, char **argv)
 static
 char* make_new_name_unlzma(char *filename)
 {
-       char *extension = strrchr(filename, '.');
-       if (!extension || strcmp(extension, ".lzma") != 0)
-               return NULL;
-       *extension = '\0';
-       return filename;
+       return make_new_name_generic(filename, "lzma");
 }
 
 static
-USE_DESKTOP(long long) int unpack_unlzma(void)
+IF_DESKTOP(long long) int unpack_unlzma(unpack_info_t *info UNUSED_PARAM)
 {
-       return unlzma(STDIN_FILENO, STDOUT_FILENO);
+       return unpack_lzma_stream(STDIN_FILENO, STDOUT_FILENO);
 }
 
-int unlzma_main(int argc, char **argv);
-int unlzma_main(int argc, char **argv)
+int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int unlzma_main(int argc UNUSED_PARAM, char **argv)
 {
-       getopt32(argc, argv, "c");
+       getopt32(argv, "cf");
        argv += optind;
        /* lzmacat? */
        if (applet_name[4] == 'c')
                option_mask32 |= OPT_STDOUT;
 
-       return unpack(argv, make_new_name_unlzma, unpack_unlzma);
+       return bbunpack(argv, make_new_name_unlzma, unpack_unlzma);
 }
 
 #endif
 
 
-/* vi: set sw=4 ts=4: */
 /*
  *     Uncompress applet for busybox (c) 2002 Glenn McGrath
  *
@@ -314,33 +356,29 @@ int unlzma_main(int argc, char **argv)
 static
 char* make_new_name_uncompress(char *filename)
 {
-       char *extension = strrchr(filename, '.');
-       if (!extension || strcmp(extension, ".Z") != 0)
-               return NULL;
-       *extension = '\0';
-       return filename;
+       return make_new_name_generic(filename, "Z");
 }
 
 static
-USE_DESKTOP(long long) int unpack_uncompress(void)
+IF_DESKTOP(long long) int unpack_uncompress(unpack_info_t *info UNUSED_PARAM)
 {
-       USE_DESKTOP(long long) int status = -1;
+       IF_DESKTOP(long long) int status = -1;
 
        if ((xread_char(STDIN_FILENO) != 0x1f) || (xread_char(STDIN_FILENO) != 0x9d)) {
                bb_error_msg("invalid magic");
        } else {
-               status = uncompress(STDIN_FILENO, STDOUT_FILENO);
+               status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
        }
        return status;
 }
 
-int uncompress_main(int argc, char **argv);
-int uncompress_main(int argc, char **argv)
+int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uncompress_main(int argc UNUSED_PARAM, char **argv)
 {
-       getopt32(argc, argv, "cf");
+       getopt32(argv, "cf");
        argv += optind;
 
-       return unpack(argv, make_new_name_uncompress, unpack_uncompress);
+       return bbunpack(argv, make_new_name_uncompress, unpack_uncompress);
 }
 
 #endif