Home | History | Annotate | Download | only in pending
      1 /* gzip.c - gzip/gunzip/zcat
      2  *
      3  * Copyright 2017 The Android Open Source Project
      4  *
      5  * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
      6  *
      7  * todo: qtv --rsyncable
      8 
      9 // gzip.net version allows all options for all commands.
     10 USE_GZIP(NEWTOY(gzip,     "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
     11 USE_GUNZIP(NEWTOY(gunzip, "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
     12 USE_ZCAT(NEWTOY(zcat,     "cdfk123456789[-123456789]", TOYFLAG_USR|TOYFLAG_BIN))
     13 
     14 config GZIP
     15   bool "gzip"
     16   default y
     17   depends on TOYBOX_LIBZ
     18   help
     19     usage: gzip [-19cdfk] [FILE...]
     20 
     21     Compress files. With no files, compresses stdin to stdout.
     22     On success, the input files are removed and replaced by new
     23     files with the .gz suffix.
     24 
     25     -c	Output to stdout
     26     -d	Decompress (act as gunzip)
     27     -f	Force: allow overwrite of output file
     28     -k	Keep input files (default is to remove)
     29     -#	Compression level 1-9 (1:fastest, 6:default, 9:best)
     30 
     31 config GUNZIP
     32   bool "gunzip"
     33   default y
     34   depends on TOYBOX_LIBZ
     35   help
     36     usage: gunzip [-cfk] [FILE...]
     37 
     38     Decompress files. With no files, decompresses stdin to stdout.
     39     On success, the input files are removed and replaced by new
     40     files without the .gz suffix.
     41 
     42     -c	Output to stdout (act as zcat)
     43     -f	Force: allow read from tty
     44     -k	Keep input files (default is to remove)
     45 
     46 config ZCAT
     47   bool "zcat"
     48   default y
     49   depends on TOYBOX_LIBZ
     50   help
     51     usage: zcat [FILE...]
     52 
     53     Decompress files to stdout. Like `gzip -dc`.
     54 
     55     -f	Force: allow read from tty
     56 */
     57 
     58 #define FORCE_FLAGS
     59 #define FOR_gzip
     60 #include "toys.h"
     61 
     62 #include <zlib.h>
     63 
     64 GLOBALS(
     65   int level;
     66 )
     67 
     68 static void fix_time(const char *path, struct stat *sb)
     69 {
     70   struct timespec times[] = { sb->st_atim, sb->st_mtim };
     71 
     72   if (utimensat(AT_FDCWD, path, times, 0)) perror_exit("utimensat");
     73 }
     74 
     75 static int do_zlib(int in_fd, int out_fd)
     76 {
     77   int len, err = 0, dd = toys.optflags&FLAG_d;
     78   char *b = "r";
     79   gzFile gz;
     80 
     81   if (!dd) {
     82     sprintf(b = toybuf, "w%d", TT.level);
     83     if (out_fd == 1) out_fd = xdup(out_fd);
     84   }
     85   if (!(gz = gzdopen(dd ? in_fd : out_fd, b))) perror_exit("gzdopen");
     86   if (dd) {
     87     while ((len = gzread(gz, toybuf, sizeof(toybuf))) > 0)
     88       if (len != writeall(out_fd, toybuf, len)) break;
     89   } else {
     90     while ((len = read(in_fd, toybuf, sizeof(toybuf))) > 0)
     91       if (len != gzwrite(gz, toybuf, len))  break;
     92   }
     93 
     94   err = !!len;
     95   if (len>0 || err == Z_ERRNO) perror_msg(dd ? "write" : "read");
     96   if (len<0)
     97     error_msg("%s%s: %s", "gz", dd ? "read" : "write", gzerror(gz, &len));
     98 
     99   if (gzclose(gz) != Z_OK) perror_msg("gzclose"), err++;
    100 
    101   return err;
    102 }
    103 
    104 static void do_gzip(int in_fd, char *arg)
    105 {
    106   struct stat sb;
    107   int len, out_fd = 0;
    108   char *out_name = 0;
    109 
    110   // Are we writing to stdout?
    111   if (!in_fd || (toys.optflags&FLAG_c)) out_fd = 1;
    112   if (isatty(in_fd)) {
    113     if (!(toys.optflags&FLAG_f)) {
    114       error_msg("%s:need -f to read TTY"+3*!!in_fd, arg);
    115       return;
    116     } else out_fd = 1;
    117   }
    118 
    119   // Are we reading file.gz to write to file?
    120   if (!out_fd) {
    121     if (fstat(in_fd, &sb)) {
    122       perror_msg("%s", arg);
    123       return;
    124     }
    125 
    126     if (!(toys.optflags&FLAG_d)) out_name = xmprintf("%s%s", arg, ".gz");
    127     else {
    128       // "gunzip x.gz" will decompress "x.gz" to "x".
    129       if ((len = strlen(arg))<4 || strcmp(arg+len-3, ".gz")) {
    130         error_msg("no .gz: %s", arg);
    131         return;
    132       }
    133       out_name = xstrdup(arg);
    134       out_name[len-3] = 0;
    135     }
    136 
    137     out_fd = xcreate(out_name,
    138       O_CREAT|O_WRONLY|WARN_ONLY|(O_EXCL*!(toys.optflags&FLAG_f)), sb.st_mode);
    139     if (out_fd == -1) return;
    140   }
    141 
    142 //  if (CFG_TOYBOX_LIBZ)
    143     if (do_zlib(in_fd, out_fd) && out_name) arg = out_name;
    144   if (out_fd != 1) close(out_fd);
    145 
    146   if (out_name) {
    147     fix_time(out_name, &sb);
    148     if (!(toys.optflags&FLAG_k)) if (unlink(arg)) perror_msg("unlink %s", arg);
    149     free(out_name);
    150   }
    151 }
    152 
    153 void gzip_main(void)
    154 {
    155   for (TT.level = 0; TT.level<9; TT.level++)
    156     if ((toys.optflags>>TT.level)&1) break;
    157   if (!(TT.level = 9-TT.level)) TT.level = 6;
    158 
    159   loopfiles(toys.optargs, do_gzip);
    160 }
    161 
    162 void gunzip_main(void)
    163 {
    164   toys.optflags |= FLAG_d;
    165   gzip_main();
    166 }
    167 
    168 void zcat_main(void)
    169 {
    170   toys.optflags |= (FLAG_c|FLAG_d);
    171   gzip_main();
    172 }
    173