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 n
17 help
18 usage: gzip [-19cdfk] [FILE...]
19
20 Compress files. With no files, compresses stdin to stdout.
21 On success, the input files are removed and replaced by new
22 files with the .gz suffix.
23
24 -c Output to stdout
25 -d Decompress (act as gunzip)
26 -f Force: allow overwrite of output file
27 -k Keep input files (default is to remove)
28 -# Compression level 1-9 (1:fastest, 6:default, 9:best)
29
30 config GUNZIP
31 bool "gunzip"
32 default n
33 help
34 usage: gunzip [-cfk] [FILE...]
35
36 Decompress files. With no files, decompresses stdin to stdout.
37 On success, the input files are removed and replaced by new
38 files without the .gz suffix.
39
40 -c Output to stdout (act as zcat)
41 -f Force: allow read from tty
42 -k Keep input files (default is to remove)
43
44 config ZCAT
45 bool "zcat"
46 default n
47 help
48 usage: zcat [FILE...]
49
50 Decompress files to stdout. Like `gzip -dc`.
51
52 -f Force: allow read from tty
53 */
54
55 #define FORCE_FLAGS
56 #define FOR_gzip
57 #include "toys.h"
58
GLOBALS(int level;)59 GLOBALS(
60 int level;
61 )
62
63 // Use assembly optimized zlib code?
64 #if CFG_TOYBOX_LIBZ
65 #include <zlib.h>
66
67 // Read fron in_fd, write to out_fd, decompress if dd else compress
68 static int do_deflate(int in_fd, int out_fd, int dd, int level)
69 {
70 int len, err = 0;
71 char *b = "r";
72 gzFile gz;
73
74 if (!dd) {
75 sprintf(b = toybuf, "w%d", level);
76 if (out_fd == 1) out_fd = xdup(out_fd);
77 }
78 if (!(gz = gzdopen(dd ? in_fd : out_fd, b))) perror_exit("gzdopen");
79 if (dd) {
80 while ((len = gzread(gz, toybuf, sizeof(toybuf))) > 0)
81 if (len != writeall(out_fd, toybuf, len)) break;
82 } else {
83 while ((len = read(in_fd, toybuf, sizeof(toybuf))) > 0)
84 if (len != gzwrite(gz, toybuf, len)) break;
85 }
86
87 err = !!len;
88 if (len>0 || err == Z_ERRNO) perror_msg(dd ? "write" : "read");
89 if (len<0)
90 error_msg("%s%s: %s", "gz", dd ? "read" : "write", gzerror(gz, &len));
91
92 if (gzclose(gz) != Z_OK) perror_msg("gzclose"), err++;
93
94 return err;
95 }
96
97 // Use toybox's builtin lib/deflate.c
98 #else
99
100 // Read from in_fd, write to out_fd, decompress if dd else compress
101 static int do_deflate(int in_fd, int out_fd, int dd, int level)
102 {
103 int x;
104
105 if (dd) WOULD_EXIT(x, gunzip_fd(in_fd, out_fd));
106 else WOULD_EXIT(x, gzip_fd(in_fd, out_fd));
107
108 return x;
109 }
110
111 #endif
112
do_gzip(int in_fd,char * arg)113 static void do_gzip(int in_fd, char *arg)
114 {
115 struct stat sb;
116 int len, out_fd = 0;
117 char *out_name = 0;
118
119 // Are we writing to stdout?
120 if (!in_fd || (toys.optflags&FLAG_c)) out_fd = 1;
121 if (isatty(in_fd)) {
122 if (!(toys.optflags&FLAG_f))
123 return error_msg("%s:need -f to read TTY"+3*!!in_fd, arg);
124 else out_fd = 1;
125 }
126
127 // Are we reading file.gz to write to file?
128 if (!out_fd) {
129 if (fstat(in_fd, &sb)) return perror_msg("%s", arg);
130
131 if (!(toys.optflags&FLAG_d)) out_name = xmprintf("%s%s", arg, ".gz");
132 else {
133 // "gunzip x.gz" will decompress "x.gz" to "x".
134 if ((len = strlen(arg))<4 || strcmp(arg+len-3, ".gz"))
135 return error_msg("no .gz: %s", arg);
136 out_name = xstrdup(arg);
137 out_name[len-3] = 0;
138 }
139
140 out_fd = xcreate(out_name,
141 O_CREAT|O_WRONLY|WARN_ONLY|(O_EXCL*!(toys.optflags&FLAG_f)), sb.st_mode);
142 if (out_fd == -1) return;
143 }
144
145 if (do_deflate(in_fd, out_fd, toys.optflags&FLAG_d, TT.level) && out_name)
146 arg = out_name;
147 if (out_fd != 1) close(out_fd);
148
149 if (out_name) {
150 struct timespec times[] = { sb.st_atim, sb.st_mtim };
151
152 if (utimensat(AT_FDCWD, out_name, times, 0)) perror_exit("utimensat");
153 if (!(toys.optflags&FLAG_k)) if (unlink(arg)) perror_msg("unlink %s", arg);
154 free(out_name);
155 }
156 }
157
gzip_main(void)158 void gzip_main(void)
159 {
160 // This depends on 1-9 being at the end of the option list
161 for (TT.level = 0; TT.level<9; TT.level++)
162 if ((toys.optflags>>TT.level)&1) break;
163 if (!(TT.level = 9-TT.level)) TT.level = 6;
164
165 loopfiles(toys.optargs, do_gzip);
166 }
167
gunzip_main(void)168 void gunzip_main(void)
169 {
170 toys.optflags |= FLAG_d;
171 gzip_main();
172 }
173
zcat_main(void)174 void zcat_main(void)
175 {
176 toys.optflags |= (FLAG_c|FLAG_d);
177 gzip_main();
178 }
179