summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes2017-04-10 12:56:40 -0500
committerElliott Hughes2017-04-10 12:56:40 -0500
commit455567f78c514b0547466edf635b57116b89f71b (patch)
tree531b154030da00863e61cde5447b60127f6c95f8 /toolbox/gzip.c
parentebc87c98e35327e7403ed767c935a8e6a02b6f03 (diff)
downloadplatform-system-core-455567f78c514b0547466edf635b57116b89f71b.tar.gz
platform-system-core-455567f78c514b0547466edf635b57116b89f71b.tar.xz
platform-system-core-455567f78c514b0547466edf635b57116b89f71b.zip
Add a reasonably full implementation of gzip/gunzip/zcat.
Bug: http://b/36653902 Test: ran tests Change-Id: Ibda577ba1d2aea5c4796eb632edd4c6d5e50592e
Diffstat (limited to 'toolbox/gzip.c')
-rw-r--r--toolbox/gzip.c261
1 files changed, 261 insertions, 0 deletions
diff --git a/toolbox/gzip.c b/toolbox/gzip.c
new file mode 100644
index 000000000..62c451830
--- /dev/null
+++ b/toolbox/gzip.c
@@ -0,0 +1,261 @@
1/* gzip.c - gzip/gunzip/zcat tools for gzip data
2 *
3 * Copyright 2017 The Android Open Source Project
4 *
5 * GZIP RFC: http://www.ietf.org/rfc/rfc1952.txt
6
7TODO: port to toybox.
8
9*/
10
11#define _GNU_SOURCE
12
13#include <errno.h>
14#include <error.h>
15#include <fcntl.h>
16#include <getopt.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <sys/stat.h>
21
22#include <zlib.h>
23
24// toybox-style flags/globals.
25#define FLAG_c 1
26#define FLAG_d 2
27#define FLAG_f 4
28#define FLAG_k 8
29static struct {
30 int optflags;
31} toys;
32static struct {
33 int level;
34} TT;
35
36static void xstat(const char *path, struct stat *sb)
37{
38 if (stat(path, sb)) error(1, errno, "stat %s", path);
39}
40
41static void fix_time(const char *path, struct stat *sb)
42{
43 struct timespec times[] = { sb->st_atim, sb->st_mtim };
44
45 if (utimensat(AT_FDCWD, path, times, 0)) error(1, errno, "utimes");
46}
47
48static FILE *xfdopen(const char *name, int flags, mode_t open_mode,
49 const char *mode)
50{
51 FILE *fp;
52 int fd;
53
54 if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
55 else fd = open(name, flags, open_mode);
56
57 if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
58 fp = fdopen(fd, mode);
59 if (fp == NULL) error(1, errno, "fopen %s (%s)", name, mode);
60 return fp;
61}
62
63static gzFile xgzopen(const char *name, int flags, mode_t open_mode,
64 const char *mode)
65{
66 gzFile f;
67 int fd;
68
69 if (!strcmp(name, "-")) fd = dup((*mode == 'r') ? 0 : 1);
70 else fd = open(name, flags, open_mode);
71
72 if (fd == -1) error(1, errno, "open %s (%s)", name, mode);
73 f = gzdopen(fd, mode);
74 if (f == NULL) error(1, errno, "gzdopen %s (%s)", name, mode);
75 return f;
76}
77
78static void gzfatal(gzFile f, char *what)
79{
80 int err;
81 const char *msg = gzerror(f, &err);
82
83 error(1, (err == Z_ERRNO) ? errno : 0, "%s: %s", what, msg);
84}
85
86static void gunzip(char *arg)
87{
88 struct stat sb;
89 char buf[BUFSIZ];
90 int len, both_files;
91 char *in_name, *out_name;
92 gzFile in;
93 FILE *out;
94
95 // "gunzip x.gz" will decompress "x.gz" to "x".
96 len = strlen(arg);
97 if (len > 3 && !strcmp(arg+len-3, ".gz")) {
98 in_name = strdup(arg);
99 out_name = strdup(arg);
100 out_name[len-3] = '\0';
101 } else if (!strcmp(arg, "-")) {
102 // "-" means stdin; assume output to stdout.
103 // TODO: require -f to read compressed data from tty?
104 in_name = strdup("-");
105 out_name = strdup("-");
106 } else error(1, 0, "unknown suffix");
107
108 if (toys.optflags&FLAG_c) {
109 free(out_name);
110 out_name = strdup("-");
111 }
112
113 both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
114 if (both_files) xstat(in_name, &sb);
115
116 in = xgzopen(in_name, O_RDONLY, 0, "r");
117 out = xfdopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
118 both_files?sb.st_mode:0666, "w");
119
120 while ((len = gzread(in, buf, sizeof(buf))) > 0) {
121 if (fwrite(buf, 1, len, out) != (size_t) len) error(1, errno, "fwrite");
122 }
123 if (len < 0) gzfatal(in, "gzread");
124 if (fclose(out)) error(1, errno, "fclose");
125 if (gzclose(in) != Z_OK) error(1, 0, "gzclose");
126
127 if (both_files) fix_time(out_name, &sb);
128 if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
129 free(in_name);
130 free(out_name);
131}
132
133static void gzip(char *in_name)
134{
135 char buf[BUFSIZ];
136 size_t len;
137 char *out_name;
138 FILE *in;
139 gzFile out;
140 struct stat sb;
141 int both_files;
142
143 if (toys.optflags&FLAG_c) {
144 out_name = strdup("-");
145 } else {
146 if (asprintf(&out_name, "%s.gz", in_name) == -1) {
147 error(1, errno, "asprintf");
148 }
149 }
150
151 both_files = strcmp(in_name, "-") && strcmp(out_name, "-");
152 if (both_files) xstat(in_name, &sb);
153
154 snprintf(buf, sizeof(buf), "w%d", TT.level);
155 in = xfdopen(in_name, O_RDONLY, 0, "r");
156 out = xgzopen(out_name, O_CREAT|O_WRONLY|((toys.optflags&FLAG_f)?0:O_EXCL),
157 both_files?sb.st_mode:0, buf);
158
159 while ((len = fread(buf, 1, sizeof(buf), in)) > 0) {
160 if (gzwrite(out, buf, len) != (int) len) gzfatal(out, "gzwrite");
161 }
162 if (ferror(in)) error(1, errno, "fread");
163 if (fclose(in)) error(1, errno, "fclose");
164 if (gzclose(out) != Z_OK) error(1, 0, "gzclose");
165
166 if (both_files) fix_time(out_name, &sb);
167 if (!(toys.optflags&(FLAG_c|FLAG_k))) unlink(in_name);
168 free(out_name);
169}
170
171static void do_file(char *arg)
172{
173 if (toys.optflags&FLAG_d) gunzip(arg);
174 else gzip(arg);
175}
176
177static void usage()
178{
179 char *cmd = basename(getprogname());
180
181 printf("usage: %s [-c] [-d] [-f] [-#] [FILE...]\n", cmd);
182 printf("\n");
183 if (!strcmp(cmd, "zcat")) {
184 printf("Decompress files to stdout. Like `gzip -dc`.\n");
185 printf("\n");
186 printf("-c\tOutput to stdout\n");
187 printf("-f\tForce: allow read from tty\n");
188 } else if (!strcmp(cmd, "gunzip")) {
189 printf("Decompress files. With no files, decompresses stdin to stdout.\n");
190 printf("On success, the input files are removed and replaced by new\n");
191 printf("files without the .gz suffix.\n");
192 printf("\n");
193 printf("-c\tOutput to stdout\n");
194 printf("-f\tForce: allow read from tty\n");
195 printf("-k\tKeep input files (don't remove)\n");
196 } else { // gzip
197 printf("Compress files. With no files, compresses stdin to stdout.\n");
198 printf("On success, the input files are removed and replaced by new\n");
199 printf("files with the .gz suffix.\n");
200 printf("\n");
201 printf("-c\tOutput to stdout\n");
202 printf("-d\tDecompress (act as gunzip)\n");
203 printf("-f\tForce: allow overwrite of output file\n");
204 printf("-k\tKeep input files (don't remove)\n");
205 printf("-#\tCompression level 1-9 (1:fastest, 6:default, 9:best)\n");
206 }
207 printf("\n");
208}
209
210int main(int argc, char *argv[])
211{
212 char *cmd = basename(argv[0]);
213 int opt_ch;
214
215 toys.optflags = 0;
216 TT.level = 6;
217
218 if (!strcmp(cmd, "gunzip")) {
219 // gunzip == gzip -d
220 toys.optflags = FLAG_d;
221 } else if (!strcmp(cmd, "zcat")) {
222 // zcat == gzip -dc
223 toys.optflags = (FLAG_c|FLAG_d);
224 }
225
226 while ((opt_ch = getopt(argc, argv, "cdfhk123456789")) != -1) {
227 switch (opt_ch) {
228 case 'c': toys.optflags |= FLAG_c; break;
229 case 'd': toys.optflags |= FLAG_d; break;
230 case 'f': toys.optflags |= FLAG_f; break;
231 case 'k': toys.optflags |= FLAG_k; break;
232
233 case '1':
234 case '2':
235 case '3':
236 case '4':
237 case '5':
238 case '6':
239 case '7':
240 case '8':
241 case '9':
242 TT.level = opt_ch - '0';
243 break;
244
245 default:
246 usage();
247 return 1;
248 }
249 }
250
251 if (optind == argc) {
252 // With no arguments, we go from stdin to stdout.
253 toys.optflags |= FLAG_c;
254 do_file("-");
255 return 0;
256 }
257
258 // Otherwise process each file in turn.
259 while (optind < argc) do_file(argv[optind++]);
260 return 0;
261}