Deleted Added
full compact
gzip.c (222287) gzip.c (226184)
1/* $NetBSD: gzip.c,v 1.99 2011/03/23 12:59:44 tsutsui Exp $ */
1/* $NetBSD: gzip.c,v 1.105 2011/08/30 23:06:00 joerg Exp $ */
2
3/*-
4 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/cdefs.h>
31#ifndef lint
32__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\
33 Matthew R. Green. All rights reserved.");
2
3/*-
4 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/cdefs.h>
31#ifndef lint
32__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\
33 Matthew R. Green. All rights reserved.");
34__FBSDID("$FreeBSD: head/usr.bin/gzip/gzip.c 222287 2011-05-25 18:04:11Z delphij $");
34__FBSDID("$FreeBSD: head/usr.bin/gzip/gzip.c 226184 2011-10-10 06:37:32Z delphij $");
35#endif /* not lint */
36
37/*
38 * gzip.c -- GPL free gzip using zlib.
39 *
40 * RFC 1950 covers the zlib format
41 * RFC 1951 covers the deflate format
42 * RFC 1952 covers the gzip format
43 *
44 * TODO:
45 * - use mmap where possible
46 * - make bzip2/compress -v/-t/-l support work as well as possible
47 */
48
49#include <sys/param.h>
50#include <sys/stat.h>
51#include <sys/time.h>
52
53#include <inttypes.h>
54#include <unistd.h>
55#include <stdio.h>
56#include <string.h>
57#include <stdlib.h>
58#include <err.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <zlib.h>
62#include <fts.h>
63#include <libgen.h>
64#include <stdarg.h>
65#include <getopt.h>
66#include <time.h>
67
68/* what type of file are we dealing with */
69enum filetype {
70 FT_GZIP,
71#ifndef NO_BZIP2_SUPPORT
72 FT_BZIP2,
73#endif
74#ifndef NO_COMPRESS_SUPPORT
75 FT_Z,
76#endif
77#ifndef NO_PACK_SUPPORT
78 FT_PACK,
79#endif
35#endif /* not lint */
36
37/*
38 * gzip.c -- GPL free gzip using zlib.
39 *
40 * RFC 1950 covers the zlib format
41 * RFC 1951 covers the deflate format
42 * RFC 1952 covers the gzip format
43 *
44 * TODO:
45 * - use mmap where possible
46 * - make bzip2/compress -v/-t/-l support work as well as possible
47 */
48
49#include <sys/param.h>
50#include <sys/stat.h>
51#include <sys/time.h>
52
53#include <inttypes.h>
54#include <unistd.h>
55#include <stdio.h>
56#include <string.h>
57#include <stdlib.h>
58#include <err.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <zlib.h>
62#include <fts.h>
63#include <libgen.h>
64#include <stdarg.h>
65#include <getopt.h>
66#include <time.h>
67
68/* what type of file are we dealing with */
69enum filetype {
70 FT_GZIP,
71#ifndef NO_BZIP2_SUPPORT
72 FT_BZIP2,
73#endif
74#ifndef NO_COMPRESS_SUPPORT
75 FT_Z,
76#endif
77#ifndef NO_PACK_SUPPORT
78 FT_PACK,
79#endif
80#ifndef NO_XZ_SUPPORT
81 FT_XZ,
82#endif
80 FT_LAST,
81 FT_UNKNOWN
82};
83
84#ifndef NO_BZIP2_SUPPORT
85#include <bzlib.h>
86
87#define BZ2_SUFFIX ".bz2"
88#define BZIP2_MAGIC "\102\132\150"
89#endif
90
91#ifndef NO_COMPRESS_SUPPORT
92#define Z_SUFFIX ".Z"
93#define Z_MAGIC "\037\235"
94#endif
95
96#ifndef NO_PACK_SUPPORT
97#define PACK_MAGIC "\037\036"
98#endif
99
83 FT_LAST,
84 FT_UNKNOWN
85};
86
87#ifndef NO_BZIP2_SUPPORT
88#include <bzlib.h>
89
90#define BZ2_SUFFIX ".bz2"
91#define BZIP2_MAGIC "\102\132\150"
92#endif
93
94#ifndef NO_COMPRESS_SUPPORT
95#define Z_SUFFIX ".Z"
96#define Z_MAGIC "\037\235"
97#endif
98
99#ifndef NO_PACK_SUPPORT
100#define PACK_MAGIC "\037\036"
101#endif
102
103#ifndef NO_XZ_SUPPORT
104#include <lzma.h>
105#define XZ_SUFFIX ".xz"
106#define XZ_MAGIC "\3757zXZ"
107#endif
108
100#define GZ_SUFFIX ".gz"
101
102#define BUFLEN (64 * 1024)
103
104#define GZIP_MAGIC0 0x1F
105#define GZIP_MAGIC1 0x8B
106#define GZIP_OMAGIC1 0x9E
107
108#define GZIP_TIMESTAMP (off_t)4
109#define GZIP_ORIGNAME (off_t)10
110
111#define HEAD_CRC 0x02
112#define EXTRA_FIELD 0x04
113#define ORIG_NAME 0x08
114#define COMMENT 0x10
115
116#define OS_CODE 3 /* Unix */
117
118typedef struct {
119 const char *zipped;
120 int ziplen;
121 const char *normal; /* for unzip - must not be longer than zipped */
122} suffixes_t;
123static suffixes_t suffixes[] = {
124#define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
125 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */
126#ifndef SMALL
127 SUFFIX(GZ_SUFFIX, ""),
128 SUFFIX(".z", ""),
129 SUFFIX("-gz", ""),
130 SUFFIX("-z", ""),
131 SUFFIX("_z", ""),
132 SUFFIX(".taz", ".tar"),
133 SUFFIX(".tgz", ".tar"),
134#ifndef NO_BZIP2_SUPPORT
135 SUFFIX(BZ2_SUFFIX, ""),
136 SUFFIX(".tbz", ".tar"),
137 SUFFIX(".tbz2", ".tar"),
138#endif
139#ifndef NO_COMPRESS_SUPPORT
140 SUFFIX(Z_SUFFIX, ""),
141#endif
109#define GZ_SUFFIX ".gz"
110
111#define BUFLEN (64 * 1024)
112
113#define GZIP_MAGIC0 0x1F
114#define GZIP_MAGIC1 0x8B
115#define GZIP_OMAGIC1 0x9E
116
117#define GZIP_TIMESTAMP (off_t)4
118#define GZIP_ORIGNAME (off_t)10
119
120#define HEAD_CRC 0x02
121#define EXTRA_FIELD 0x04
122#define ORIG_NAME 0x08
123#define COMMENT 0x10
124
125#define OS_CODE 3 /* Unix */
126
127typedef struct {
128 const char *zipped;
129 int ziplen;
130 const char *normal; /* for unzip - must not be longer than zipped */
131} suffixes_t;
132static suffixes_t suffixes[] = {
133#define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
134 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */
135#ifndef SMALL
136 SUFFIX(GZ_SUFFIX, ""),
137 SUFFIX(".z", ""),
138 SUFFIX("-gz", ""),
139 SUFFIX("-z", ""),
140 SUFFIX("_z", ""),
141 SUFFIX(".taz", ".tar"),
142 SUFFIX(".tgz", ".tar"),
143#ifndef NO_BZIP2_SUPPORT
144 SUFFIX(BZ2_SUFFIX, ""),
145 SUFFIX(".tbz", ".tar"),
146 SUFFIX(".tbz2", ".tar"),
147#endif
148#ifndef NO_COMPRESS_SUPPORT
149 SUFFIX(Z_SUFFIX, ""),
150#endif
151#ifndef NO_XZ_SUPPORT
152 SUFFIX(XZ_SUFFIX, ""),
153#endif
142 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */
143#endif /* SMALL */
144#undef SUFFIX
145};
146#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
147#define SUFFIX_MAXLEN 30
148
154 SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */
155#endif /* SMALL */
156#undef SUFFIX
157};
158#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
159#define SUFFIX_MAXLEN 30
160
149static const char gzip_version[] = "FreeBSD gzip 20110523";
161static const char gzip_version[] = "FreeBSD gzip 20111009";
150
151#ifndef SMALL
152static const char gzip_copyright[] = \
153" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
154" All rights reserved.\n"
155"\n"
156" Redistribution and use in source and binary forms, with or without\n"
157" modification, are permitted provided that the following conditions\n"
158" are met:\n"
159" 1. Redistributions of source code must retain the above copyright\n"
160" notice, this list of conditions and the following disclaimer.\n"
161" 2. Redistributions in binary form must reproduce the above copyright\n"
162" notice, this list of conditions and the following disclaimer in the\n"
163" documentation and/or other materials provided with the distribution.\n"
164"\n"
165" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
166" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
167" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
168" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
169" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
170" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
171" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
172" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
173" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
174" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
175" SUCH DAMAGE.";
176#endif
177
178static int cflag; /* stdout mode */
179static int dflag; /* decompress mode */
180static int lflag; /* list mode */
181static int numflag = 6; /* gzip -1..-9 value */
182
183#ifndef SMALL
184static int fflag; /* force mode */
185static int kflag; /* don't delete input files */
186static int nflag; /* don't save name/timestamp */
187static int Nflag; /* don't restore name/timestamp */
188static int qflag; /* quiet mode */
189static int rflag; /* recursive mode */
190static int tflag; /* test */
191static int vflag; /* verbose mode */
192static const char *remove_file = NULL; /* file to be removed upon SIGINT */
193#else
194#define qflag 0
195#define tflag 0
196#endif
197
198static int exit_value = 0; /* exit value */
199
200static char *infile; /* name of file coming in */
201
162
163#ifndef SMALL
164static const char gzip_copyright[] = \
165" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
166" All rights reserved.\n"
167"\n"
168" Redistribution and use in source and binary forms, with or without\n"
169" modification, are permitted provided that the following conditions\n"
170" are met:\n"
171" 1. Redistributions of source code must retain the above copyright\n"
172" notice, this list of conditions and the following disclaimer.\n"
173" 2. Redistributions in binary form must reproduce the above copyright\n"
174" notice, this list of conditions and the following disclaimer in the\n"
175" documentation and/or other materials provided with the distribution.\n"
176"\n"
177" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
178" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
179" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
180" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
181" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
182" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
183" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
184" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
185" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
186" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
187" SUCH DAMAGE.";
188#endif
189
190static int cflag; /* stdout mode */
191static int dflag; /* decompress mode */
192static int lflag; /* list mode */
193static int numflag = 6; /* gzip -1..-9 value */
194
195#ifndef SMALL
196static int fflag; /* force mode */
197static int kflag; /* don't delete input files */
198static int nflag; /* don't save name/timestamp */
199static int Nflag; /* don't restore name/timestamp */
200static int qflag; /* quiet mode */
201static int rflag; /* recursive mode */
202static int tflag; /* test */
203static int vflag; /* verbose mode */
204static const char *remove_file = NULL; /* file to be removed upon SIGINT */
205#else
206#define qflag 0
207#define tflag 0
208#endif
209
210static int exit_value = 0; /* exit value */
211
212static char *infile; /* name of file coming in */
213
202static void maybe_err(const char *fmt, ...) __dead2
203 __attribute__((__format__(__printf__, 1, 2)));
204#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT)
205static void maybe_errx(const char *fmt, ...) __dead2
206 __attribute__((__format__(__printf__, 1, 2)));
214static void maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2;
215#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
216 !defined(NO_XZ_SUPPORT)
217static void maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2;
207#endif
218#endif
208static void maybe_warn(const char *fmt, ...)
209 __attribute__((__format__(__printf__, 1, 2)));
210static void maybe_warnx(const char *fmt, ...)
211 __attribute__((__format__(__printf__, 1, 2)));
219static void maybe_warn(const char *fmt, ...) __printflike(1, 2);
220static void maybe_warnx(const char *fmt, ...) __printflike(1, 2);
212static enum filetype file_gettype(u_char *);
213#ifdef SMALL
214#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
215#endif
216static off_t gz_compress(int, int, off_t *, const char *, uint32_t);
217static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *);
218static off_t file_compress(char *, char *, size_t);
219static off_t file_uncompress(char *, char *, size_t);
220static void handle_pathname(char *);
221static void handle_file(char *, struct stat *);
222static void handle_stdin(void);
223static void handle_stdout(void);
224static void print_ratio(off_t, off_t, FILE *);
225static void print_list(int fd, off_t, const char *, time_t);
221static enum filetype file_gettype(u_char *);
222#ifdef SMALL
223#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz)
224#endif
225static off_t gz_compress(int, int, off_t *, const char *, uint32_t);
226static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *);
227static off_t file_compress(char *, char *, size_t);
228static off_t file_uncompress(char *, char *, size_t);
229static void handle_pathname(char *);
230static void handle_file(char *, struct stat *);
231static void handle_stdin(void);
232static void handle_stdout(void);
233static void print_ratio(off_t, off_t, FILE *);
234static void print_list(int fd, off_t, const char *, time_t);
226static void usage(void);
227static void display_version(void);
235static void usage(void) __dead2;
236static void display_version(void) __dead2;
228#ifndef SMALL
229static void display_license(void);
230static void sigint_handler(int);
231#endif
232static const suffixes_t *check_suffix(char *, int);
233static ssize_t read_retry(int, void *, size_t);
234
235#ifdef SMALL
236#define unlink_input(f, sb) unlink(f)
237#else
238static off_t cat_fd(unsigned char *, size_t, off_t *, int fd);
239static void prepend_gzip(char *, int *, char ***);
240static void handle_dir(char *);
241static void print_verbage(const char *, const char *, off_t, off_t);
242static void print_test(const char *, int);
243static void copymodes(int fd, const struct stat *, const char *file);
244static int check_outfile(const char *outfile);
245#endif
246
247#ifndef NO_BZIP2_SUPPORT
248static off_t unbzip2(int, int, char *, size_t, off_t *);
249#endif
250
251#ifndef NO_COMPRESS_SUPPORT
252static FILE *zdopen(int);
253static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *);
254#endif
255
256#ifndef NO_PACK_SUPPORT
257static off_t unpack(int, int, char *, size_t, off_t *);
258#endif
259
237#ifndef SMALL
238static void display_license(void);
239static void sigint_handler(int);
240#endif
241static const suffixes_t *check_suffix(char *, int);
242static ssize_t read_retry(int, void *, size_t);
243
244#ifdef SMALL
245#define unlink_input(f, sb) unlink(f)
246#else
247static off_t cat_fd(unsigned char *, size_t, off_t *, int fd);
248static void prepend_gzip(char *, int *, char ***);
249static void handle_dir(char *);
250static void print_verbage(const char *, const char *, off_t, off_t);
251static void print_test(const char *, int);
252static void copymodes(int fd, const struct stat *, const char *file);
253static int check_outfile(const char *outfile);
254#endif
255
256#ifndef NO_BZIP2_SUPPORT
257static off_t unbzip2(int, int, char *, size_t, off_t *);
258#endif
259
260#ifndef NO_COMPRESS_SUPPORT
261static FILE *zdopen(int);
262static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *);
263#endif
264
265#ifndef NO_PACK_SUPPORT
266static off_t unpack(int, int, char *, size_t, off_t *);
267#endif
268
260int main(int, char **p);
269#ifndef NO_XZ_SUPPORT
270static off_t unxz(int, int, char *, size_t, off_t *);
271#endif
261
262#ifdef SMALL
263#define getopt_long(a,b,c,d,e) getopt(a,b,c)
264#else
265static const struct option longopts[] = {
266 { "stdout", no_argument, 0, 'c' },
267 { "to-stdout", no_argument, 0, 'c' },
268 { "decompress", no_argument, 0, 'd' },
269 { "uncompress", no_argument, 0, 'd' },
270 { "force", no_argument, 0, 'f' },
271 { "help", no_argument, 0, 'h' },
272 { "keep", no_argument, 0, 'k' },
273 { "list", no_argument, 0, 'l' },
274 { "no-name", no_argument, 0, 'n' },
275 { "name", no_argument, 0, 'N' },
276 { "quiet", no_argument, 0, 'q' },
277 { "recursive", no_argument, 0, 'r' },
278 { "suffix", required_argument, 0, 'S' },
279 { "test", no_argument, 0, 't' },
280 { "verbose", no_argument, 0, 'v' },
281 { "version", no_argument, 0, 'V' },
282 { "fast", no_argument, 0, '1' },
283 { "best", no_argument, 0, '9' },
284 { "ascii", no_argument, 0, 'a' },
285 { "license", no_argument, 0, 'L' },
286 { NULL, no_argument, 0, 0 },
287};
288#endif
289
290int
291main(int argc, char **argv)
292{
293 const char *progname = getprogname();
294#ifndef SMALL
295 char *gzip;
296 int len;
297#endif
298 int ch;
299
300#ifndef SMALL
301 if ((gzip = getenv("GZIP")) != NULL)
302 prepend_gzip(gzip, &argc, &argv);
303 signal(SIGINT, sigint_handler);
304#endif
305
306 /*
307 * XXX
308 * handle being called `gunzip', `zcat' and `gzcat'
309 */
310 if (strcmp(progname, "gunzip") == 0)
311 dflag = 1;
312 else if (strcmp(progname, "zcat") == 0 ||
313 strcmp(progname, "gzcat") == 0)
314 dflag = cflag = 1;
315
316#ifdef SMALL
317#define OPT_LIST "123456789cdhlV"
318#else
319#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
320#endif
321
322 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
323 switch (ch) {
324 case '1': case '2': case '3':
325 case '4': case '5': case '6':
326 case '7': case '8': case '9':
327 numflag = ch - '0';
328 break;
329 case 'c':
330 cflag = 1;
331 break;
332 case 'd':
333 dflag = 1;
334 break;
335 case 'l':
336 lflag = 1;
337 dflag = 1;
338 break;
339 case 'V':
340 display_version();
341 /* NOTREACHED */
342#ifndef SMALL
343 case 'a':
344 fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
345 break;
346 case 'f':
347 fflag = 1;
348 break;
349 case 'k':
350 kflag = 1;
351 break;
352 case 'L':
353 display_license();
354 /* NOT REACHED */
355 case 'N':
356 nflag = 0;
357 Nflag = 1;
358 break;
359 case 'n':
360 nflag = 1;
361 Nflag = 0;
362 break;
363 case 'q':
364 qflag = 1;
365 break;
366 case 'r':
367 rflag = 1;
368 break;
369 case 'S':
370 len = strlen(optarg);
371 if (len != 0) {
372 if (len > SUFFIX_MAXLEN)
373 errx(1, "incorrect suffix: '%s': too long", optarg);
374 suffixes[0].zipped = optarg;
375 suffixes[0].ziplen = len;
376 } else {
377 suffixes[NUM_SUFFIXES - 1].zipped = "";
378 suffixes[NUM_SUFFIXES - 1].ziplen = 0;
379 }
380 break;
381 case 't':
382 cflag = 1;
383 tflag = 1;
384 dflag = 1;
385 break;
386 case 'v':
387 vflag = 1;
388 break;
389#endif
390 default:
391 usage();
392 /* NOTREACHED */
393 }
394 }
395 argv += optind;
396 argc -= optind;
397
398 if (argc == 0) {
399 if (dflag) /* stdin mode */
400 handle_stdin();
401 else /* stdout mode */
402 handle_stdout();
403 } else {
404 do {
405 handle_pathname(argv[0]);
406 } while (*++argv);
407 }
408#ifndef SMALL
409 if (qflag == 0 && lflag && argc > 1)
410 print_list(-1, 0, "(totals)", 0);
411#endif
412 exit(exit_value);
413}
414
415/* maybe print a warning */
416void
417maybe_warn(const char *fmt, ...)
418{
419 va_list ap;
420
421 if (qflag == 0) {
422 va_start(ap, fmt);
423 vwarn(fmt, ap);
424 va_end(ap);
425 }
426 if (exit_value == 0)
427 exit_value = 1;
428}
429
430/* ... without an errno. */
431void
432maybe_warnx(const char *fmt, ...)
433{
434 va_list ap;
435
436 if (qflag == 0) {
437 va_start(ap, fmt);
438 vwarnx(fmt, ap);
439 va_end(ap);
440 }
441 if (exit_value == 0)
442 exit_value = 1;
443}
444
445/* maybe print an error */
446void
447maybe_err(const char *fmt, ...)
448{
449 va_list ap;
450
451 if (qflag == 0) {
452 va_start(ap, fmt);
453 vwarn(fmt, ap);
454 va_end(ap);
455 }
456 exit(2);
457}
458
272
273#ifdef SMALL
274#define getopt_long(a,b,c,d,e) getopt(a,b,c)
275#else
276static const struct option longopts[] = {
277 { "stdout", no_argument, 0, 'c' },
278 { "to-stdout", no_argument, 0, 'c' },
279 { "decompress", no_argument, 0, 'd' },
280 { "uncompress", no_argument, 0, 'd' },
281 { "force", no_argument, 0, 'f' },
282 { "help", no_argument, 0, 'h' },
283 { "keep", no_argument, 0, 'k' },
284 { "list", no_argument, 0, 'l' },
285 { "no-name", no_argument, 0, 'n' },
286 { "name", no_argument, 0, 'N' },
287 { "quiet", no_argument, 0, 'q' },
288 { "recursive", no_argument, 0, 'r' },
289 { "suffix", required_argument, 0, 'S' },
290 { "test", no_argument, 0, 't' },
291 { "verbose", no_argument, 0, 'v' },
292 { "version", no_argument, 0, 'V' },
293 { "fast", no_argument, 0, '1' },
294 { "best", no_argument, 0, '9' },
295 { "ascii", no_argument, 0, 'a' },
296 { "license", no_argument, 0, 'L' },
297 { NULL, no_argument, 0, 0 },
298};
299#endif
300
301int
302main(int argc, char **argv)
303{
304 const char *progname = getprogname();
305#ifndef SMALL
306 char *gzip;
307 int len;
308#endif
309 int ch;
310
311#ifndef SMALL
312 if ((gzip = getenv("GZIP")) != NULL)
313 prepend_gzip(gzip, &argc, &argv);
314 signal(SIGINT, sigint_handler);
315#endif
316
317 /*
318 * XXX
319 * handle being called `gunzip', `zcat' and `gzcat'
320 */
321 if (strcmp(progname, "gunzip") == 0)
322 dflag = 1;
323 else if (strcmp(progname, "zcat") == 0 ||
324 strcmp(progname, "gzcat") == 0)
325 dflag = cflag = 1;
326
327#ifdef SMALL
328#define OPT_LIST "123456789cdhlV"
329#else
330#define OPT_LIST "123456789acdfhklLNnqrS:tVv"
331#endif
332
333 while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
334 switch (ch) {
335 case '1': case '2': case '3':
336 case '4': case '5': case '6':
337 case '7': case '8': case '9':
338 numflag = ch - '0';
339 break;
340 case 'c':
341 cflag = 1;
342 break;
343 case 'd':
344 dflag = 1;
345 break;
346 case 'l':
347 lflag = 1;
348 dflag = 1;
349 break;
350 case 'V':
351 display_version();
352 /* NOTREACHED */
353#ifndef SMALL
354 case 'a':
355 fprintf(stderr, "%s: option --ascii ignored on this system\n", progname);
356 break;
357 case 'f':
358 fflag = 1;
359 break;
360 case 'k':
361 kflag = 1;
362 break;
363 case 'L':
364 display_license();
365 /* NOT REACHED */
366 case 'N':
367 nflag = 0;
368 Nflag = 1;
369 break;
370 case 'n':
371 nflag = 1;
372 Nflag = 0;
373 break;
374 case 'q':
375 qflag = 1;
376 break;
377 case 'r':
378 rflag = 1;
379 break;
380 case 'S':
381 len = strlen(optarg);
382 if (len != 0) {
383 if (len > SUFFIX_MAXLEN)
384 errx(1, "incorrect suffix: '%s': too long", optarg);
385 suffixes[0].zipped = optarg;
386 suffixes[0].ziplen = len;
387 } else {
388 suffixes[NUM_SUFFIXES - 1].zipped = "";
389 suffixes[NUM_SUFFIXES - 1].ziplen = 0;
390 }
391 break;
392 case 't':
393 cflag = 1;
394 tflag = 1;
395 dflag = 1;
396 break;
397 case 'v':
398 vflag = 1;
399 break;
400#endif
401 default:
402 usage();
403 /* NOTREACHED */
404 }
405 }
406 argv += optind;
407 argc -= optind;
408
409 if (argc == 0) {
410 if (dflag) /* stdin mode */
411 handle_stdin();
412 else /* stdout mode */
413 handle_stdout();
414 } else {
415 do {
416 handle_pathname(argv[0]);
417 } while (*++argv);
418 }
419#ifndef SMALL
420 if (qflag == 0 && lflag && argc > 1)
421 print_list(-1, 0, "(totals)", 0);
422#endif
423 exit(exit_value);
424}
425
426/* maybe print a warning */
427void
428maybe_warn(const char *fmt, ...)
429{
430 va_list ap;
431
432 if (qflag == 0) {
433 va_start(ap, fmt);
434 vwarn(fmt, ap);
435 va_end(ap);
436 }
437 if (exit_value == 0)
438 exit_value = 1;
439}
440
441/* ... without an errno. */
442void
443maybe_warnx(const char *fmt, ...)
444{
445 va_list ap;
446
447 if (qflag == 0) {
448 va_start(ap, fmt);
449 vwarnx(fmt, ap);
450 va_end(ap);
451 }
452 if (exit_value == 0)
453 exit_value = 1;
454}
455
456/* maybe print an error */
457void
458maybe_err(const char *fmt, ...)
459{
460 va_list ap;
461
462 if (qflag == 0) {
463 va_start(ap, fmt);
464 vwarn(fmt, ap);
465 va_end(ap);
466 }
467 exit(2);
468}
469
459#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT)
470#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \
471 !defined(NO_XZ_SUPPORT)
460/* ... without an errno. */
461void
462maybe_errx(const char *fmt, ...)
463{
464 va_list ap;
465
466 if (qflag == 0) {
467 va_start(ap, fmt);
468 vwarnx(fmt, ap);
469 va_end(ap);
470 }
471 exit(2);
472}
473#endif
474
475#ifndef SMALL
476/* split up $GZIP and prepend it to the argument list */
477static void
478prepend_gzip(char *gzip, int *argc, char ***argv)
479{
480 char *s, **nargv, **ac;
481 int nenvarg = 0, i;
482
483 /* scan how many arguments there are */
484 for (s = gzip;;) {
485 while (*s == ' ' || *s == '\t')
486 s++;
487 if (*s == 0)
488 goto count_done;
489 nenvarg++;
490 while (*s != ' ' && *s != '\t')
491 if (*s++ == 0)
492 goto count_done;
493 }
494count_done:
495 /* punt early */
496 if (nenvarg == 0)
497 return;
498
499 *argc += nenvarg;
500 ac = *argv;
501
502 nargv = (char **)malloc((*argc + 1) * sizeof(char *));
503 if (nargv == NULL)
504 maybe_err("malloc");
505
506 /* stash this away */
507 *argv = nargv;
508
509 /* copy the program name first */
510 i = 0;
511 nargv[i++] = *(ac++);
512
513 /* take a copy of $GZIP and add it to the array */
514 s = strdup(gzip);
515 if (s == NULL)
516 maybe_err("strdup");
517 for (;;) {
518 /* Skip whitespaces. */
519 while (*s == ' ' || *s == '\t')
520 s++;
521 if (*s == 0)
522 goto copy_done;
523 nargv[i++] = s;
524 /* Find the end of this argument. */
525 while (*s != ' ' && *s != '\t')
526 if (*s++ == 0)
527 /* Argument followed by NUL. */
528 goto copy_done;
529 /* Terminate by overwriting ' ' or '\t' with NUL. */
530 *s++ = 0;
531 }
532copy_done:
533
534 /* copy the original arguments and a NULL */
535 while (*ac)
536 nargv[i++] = *(ac++);
537 nargv[i] = NULL;
538}
539#endif
540
541/* compress input to output. Return bytes read, -1 on error */
542static off_t
543gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
544{
545 z_stream z;
546 char *outbufp, *inbufp;
547 off_t in_tot = 0, out_tot = 0;
548 ssize_t in_size;
549 int i, error;
550 uLong crc;
551#ifdef SMALL
552 static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
553 0, 0, 0, 0,
554 0, OS_CODE };
555#endif
556
557 outbufp = malloc(BUFLEN);
558 inbufp = malloc(BUFLEN);
559 if (outbufp == NULL || inbufp == NULL) {
560 maybe_err("malloc failed");
561 goto out;
562 }
563
564 memset(&z, 0, sizeof z);
565 z.zalloc = Z_NULL;
566 z.zfree = Z_NULL;
567 z.opaque = 0;
568
569#ifdef SMALL
570 memcpy(outbufp, header, sizeof header);
571 i = sizeof header;
572#else
573 if (nflag != 0) {
574 mtime = 0;
575 origname = "";
576 }
577
578 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
579 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
580 *origname ? ORIG_NAME : 0,
581 mtime & 0xff,
582 (mtime >> 8) & 0xff,
583 (mtime >> 16) & 0xff,
584 (mtime >> 24) & 0xff,
585 numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
586 OS_CODE, origname);
587 if (i >= BUFLEN)
588 /* this need PATH_MAX > BUFLEN ... */
589 maybe_err("snprintf");
590 if (*origname)
591 i++;
592#endif
593
594 z.next_out = (unsigned char *)outbufp + i;
595 z.avail_out = BUFLEN - i;
596
597 error = deflateInit2(&z, numflag, Z_DEFLATED,
598 (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
599 if (error != Z_OK) {
600 maybe_warnx("deflateInit2 failed");
601 in_tot = -1;
602 goto out;
603 }
604
605 crc = crc32(0L, Z_NULL, 0);
606 for (;;) {
607 if (z.avail_out == 0) {
608 if (write(out, outbufp, BUFLEN) != BUFLEN) {
609 maybe_warn("write");
610 out_tot = -1;
611 goto out;
612 }
613
614 out_tot += BUFLEN;
615 z.next_out = (unsigned char *)outbufp;
616 z.avail_out = BUFLEN;
617 }
618
619 if (z.avail_in == 0) {
620 in_size = read(in, inbufp, BUFLEN);
621 if (in_size < 0) {
622 maybe_warn("read");
623 in_tot = -1;
624 goto out;
625 }
626 if (in_size == 0)
627 break;
628
629 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
630 in_tot += in_size;
631 z.next_in = (unsigned char *)inbufp;
632 z.avail_in = in_size;
633 }
634
635 error = deflate(&z, Z_NO_FLUSH);
636 if (error != Z_OK && error != Z_STREAM_END) {
637 maybe_warnx("deflate failed");
638 in_tot = -1;
639 goto out;
640 }
641 }
642
643 /* clean up */
644 for (;;) {
645 size_t len;
646 ssize_t w;
647
648 error = deflate(&z, Z_FINISH);
649 if (error != Z_OK && error != Z_STREAM_END) {
650 maybe_warnx("deflate failed");
651 in_tot = -1;
652 goto out;
653 }
654
655 len = (char *)z.next_out - outbufp;
656
657 w = write(out, outbufp, len);
658 if (w == -1 || (size_t)w != len) {
659 maybe_warn("write");
660 out_tot = -1;
661 goto out;
662 }
663 out_tot += len;
664 z.next_out = (unsigned char *)outbufp;
665 z.avail_out = BUFLEN;
666
667 if (error == Z_STREAM_END)
668 break;
669 }
670
671 if (deflateEnd(&z) != Z_OK) {
672 maybe_warnx("deflateEnd failed");
673 in_tot = -1;
674 goto out;
675 }
676
677 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
678 (int)crc & 0xff,
679 (int)(crc >> 8) & 0xff,
680 (int)(crc >> 16) & 0xff,
681 (int)(crc >> 24) & 0xff,
682 (int)in_tot & 0xff,
683 (int)(in_tot >> 8) & 0xff,
684 (int)(in_tot >> 16) & 0xff,
685 (int)(in_tot >> 24) & 0xff);
686 if (i != 8)
687 maybe_err("snprintf");
688 if (write(out, outbufp, i) != i) {
689 maybe_warn("write");
690 in_tot = -1;
691 } else
692 out_tot += i;
693
694out:
695 if (inbufp != NULL)
696 free(inbufp);
697 if (outbufp != NULL)
698 free(outbufp);
699 if (gsizep)
700 *gsizep = out_tot;
701 return in_tot;
702}
703
704/*
705 * uncompress input to output then close the input. return the
706 * uncompressed size written, and put the compressed sized read
707 * into `*gsizep'.
708 */
709static off_t
710gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
711 const char *filename)
712{
713 z_stream z;
714 char *outbufp, *inbufp;
715 off_t out_tot = -1, in_tot = 0;
716 uint32_t out_sub_tot = 0;
717 enum {
718 GZSTATE_MAGIC0,
719 GZSTATE_MAGIC1,
720 GZSTATE_METHOD,
721 GZSTATE_FLAGS,
722 GZSTATE_SKIPPING,
723 GZSTATE_EXTRA,
724 GZSTATE_EXTRA2,
725 GZSTATE_EXTRA3,
726 GZSTATE_ORIGNAME,
727 GZSTATE_COMMENT,
728 GZSTATE_HEAD_CRC1,
729 GZSTATE_HEAD_CRC2,
730 GZSTATE_INIT,
731 GZSTATE_READ,
732 GZSTATE_CRC,
733 GZSTATE_LEN,
734 } state = GZSTATE_MAGIC0;
735 int flags = 0, skip_count = 0;
736 int error = Z_STREAM_ERROR, done_reading = 0;
737 uLong crc = 0;
738 ssize_t wr;
739 int needmore = 0;
740
741#define ADVANCE() { z.next_in++; z.avail_in--; }
742
743 if ((outbufp = malloc(BUFLEN)) == NULL) {
744 maybe_err("malloc failed");
745 goto out2;
746 }
747 if ((inbufp = malloc(BUFLEN)) == NULL) {
748 maybe_err("malloc failed");
749 goto out1;
750 }
751
752 memset(&z, 0, sizeof z);
753 z.avail_in = prelen;
754 z.next_in = (unsigned char *)pre;
755 z.avail_out = BUFLEN;
756 z.next_out = (unsigned char *)outbufp;
757 z.zalloc = NULL;
758 z.zfree = NULL;
759 z.opaque = 0;
760
761 in_tot = prelen;
762 out_tot = 0;
763
764 for (;;) {
765 if ((z.avail_in == 0 || needmore) && done_reading == 0) {
766 ssize_t in_size;
767
768 if (z.avail_in > 0) {
769 memmove(inbufp, z.next_in, z.avail_in);
770 }
771 z.next_in = (unsigned char *)inbufp;
772 in_size = read(in, z.next_in + z.avail_in,
773 BUFLEN - z.avail_in);
774
775 if (in_size == -1) {
776 maybe_warn("failed to read stdin");
777 goto stop_and_fail;
778 } else if (in_size == 0) {
779 done_reading = 1;
780 }
781
782 z.avail_in += in_size;
783 needmore = 0;
784
785 in_tot += in_size;
786 }
787 if (z.avail_in == 0) {
788 if (done_reading && state != GZSTATE_MAGIC0) {
789 maybe_warnx("%s: unexpected end of file",
790 filename);
791 goto stop_and_fail;
792 }
793 goto stop;
794 }
795 switch (state) {
796 case GZSTATE_MAGIC0:
797 if (*z.next_in != GZIP_MAGIC0) {
798 if (in_tot > 0) {
799 maybe_warnx("%s: trailing garbage "
800 "ignored", filename);
801 goto stop;
802 }
803 maybe_warnx("input not gziped (MAGIC0)");
804 goto stop_and_fail;
805 }
806 ADVANCE();
807 state++;
808 out_sub_tot = 0;
809 crc = crc32(0L, Z_NULL, 0);
810 break;
811
812 case GZSTATE_MAGIC1:
813 if (*z.next_in != GZIP_MAGIC1 &&
814 *z.next_in != GZIP_OMAGIC1) {
815 maybe_warnx("input not gziped (MAGIC1)");
816 goto stop_and_fail;
817 }
818 ADVANCE();
819 state++;
820 break;
821
822 case GZSTATE_METHOD:
823 if (*z.next_in != Z_DEFLATED) {
824 maybe_warnx("unknown compression method");
825 goto stop_and_fail;
826 }
827 ADVANCE();
828 state++;
829 break;
830
831 case GZSTATE_FLAGS:
832 flags = *z.next_in;
833 ADVANCE();
834 skip_count = 6;
835 state++;
836 break;
837
838 case GZSTATE_SKIPPING:
839 if (skip_count > 0) {
840 skip_count--;
841 ADVANCE();
842 } else
843 state++;
844 break;
845
846 case GZSTATE_EXTRA:
847 if ((flags & EXTRA_FIELD) == 0) {
848 state = GZSTATE_ORIGNAME;
849 break;
850 }
851 skip_count = *z.next_in;
852 ADVANCE();
853 state++;
854 break;
855
856 case GZSTATE_EXTRA2:
857 skip_count |= ((*z.next_in) << 8);
858 ADVANCE();
859 state++;
860 break;
861
862 case GZSTATE_EXTRA3:
863 if (skip_count > 0) {
864 skip_count--;
865 ADVANCE();
866 } else
867 state++;
868 break;
869
870 case GZSTATE_ORIGNAME:
871 if ((flags & ORIG_NAME) == 0) {
872 state++;
873 break;
874 }
875 if (*z.next_in == 0)
876 state++;
877 ADVANCE();
878 break;
879
880 case GZSTATE_COMMENT:
881 if ((flags & COMMENT) == 0) {
882 state++;
883 break;
884 }
885 if (*z.next_in == 0)
886 state++;
887 ADVANCE();
888 break;
889
890 case GZSTATE_HEAD_CRC1:
891 if (flags & HEAD_CRC)
892 skip_count = 2;
893 else
894 skip_count = 0;
895 state++;
896 break;
897
898 case GZSTATE_HEAD_CRC2:
899 if (skip_count > 0) {
900 skip_count--;
901 ADVANCE();
902 } else
903 state++;
904 break;
905
906 case GZSTATE_INIT:
907 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
908 maybe_warnx("failed to inflateInit");
909 goto stop_and_fail;
910 }
911 state++;
912 break;
913
914 case GZSTATE_READ:
915 error = inflate(&z, Z_FINISH);
916 switch (error) {
917 /* Z_BUF_ERROR goes with Z_FINISH... */
918 case Z_BUF_ERROR:
919 if (z.avail_out > 0 && !done_reading)
920 continue;
921
922 case Z_STREAM_END:
923 case Z_OK:
924 break;
925
926 case Z_NEED_DICT:
927 maybe_warnx("Z_NEED_DICT error");
928 goto stop_and_fail;
929 case Z_DATA_ERROR:
930 maybe_warnx("data stream error");
931 goto stop_and_fail;
932 case Z_STREAM_ERROR:
933 maybe_warnx("internal stream error");
934 goto stop_and_fail;
935 case Z_MEM_ERROR:
936 maybe_warnx("memory allocation error");
937 goto stop_and_fail;
938
939 default:
940 maybe_warn("unknown error from inflate(): %d",
941 error);
942 }
943 wr = BUFLEN - z.avail_out;
944
945 if (wr != 0) {
946 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
947 if (
948#ifndef SMALL
949 /* don't write anything with -t */
950 tflag == 0 &&
951#endif
952 write(out, outbufp, wr) != wr) {
953 maybe_warn("error writing to output");
954 goto stop_and_fail;
955 }
956
957 out_tot += wr;
958 out_sub_tot += wr;
959 }
960
961 if (error == Z_STREAM_END) {
962 inflateEnd(&z);
963 state++;
964 }
965
966 z.next_out = (unsigned char *)outbufp;
967 z.avail_out = BUFLEN;
968
969 break;
970 case GZSTATE_CRC:
971 {
972 uLong origcrc;
973
974 if (z.avail_in < 4) {
975 if (!done_reading) {
976 needmore = 1;
977 continue;
978 }
979 maybe_warnx("truncated input");
980 goto stop_and_fail;
981 }
982 origcrc = ((unsigned)z.next_in[0] & 0xff) |
983 ((unsigned)z.next_in[1] & 0xff) << 8 |
984 ((unsigned)z.next_in[2] & 0xff) << 16 |
985 ((unsigned)z.next_in[3] & 0xff) << 24;
986 if (origcrc != crc) {
987 maybe_warnx("invalid compressed"
988 " data--crc error");
989 goto stop_and_fail;
990 }
991 }
992
993 z.avail_in -= 4;
994 z.next_in += 4;
995
996 if (!z.avail_in && done_reading) {
997 goto stop;
998 }
999 state++;
1000 break;
1001 case GZSTATE_LEN:
1002 {
1003 uLong origlen;
1004
1005 if (z.avail_in < 4) {
1006 if (!done_reading) {
1007 needmore = 1;
1008 continue;
1009 }
1010 maybe_warnx("truncated input");
1011 goto stop_and_fail;
1012 }
1013 origlen = ((unsigned)z.next_in[0] & 0xff) |
1014 ((unsigned)z.next_in[1] & 0xff) << 8 |
1015 ((unsigned)z.next_in[2] & 0xff) << 16 |
1016 ((unsigned)z.next_in[3] & 0xff) << 24;
1017
1018 if (origlen != out_sub_tot) {
1019 maybe_warnx("invalid compressed"
1020 " data--length error");
1021 goto stop_and_fail;
1022 }
1023 }
1024
1025 z.avail_in -= 4;
1026 z.next_in += 4;
1027
1028 if (error < 0) {
1029 maybe_warnx("decompression error");
1030 goto stop_and_fail;
1031 }
1032 state = GZSTATE_MAGIC0;
1033 break;
1034 }
1035 continue;
1036stop_and_fail:
1037 out_tot = -1;
1038stop:
1039 break;
1040 }
1041 if (state > GZSTATE_INIT)
1042 inflateEnd(&z);
1043
1044 free(inbufp);
1045out1:
1046 free(outbufp);
1047out2:
1048 if (gsizep)
1049 *gsizep = in_tot;
1050 return (out_tot);
1051}
1052
1053#ifndef SMALL
1054/*
1055 * set the owner, mode, flags & utimes using the given file descriptor.
1056 * file is only used in possible warning messages.
1057 */
1058static void
1059copymodes(int fd, const struct stat *sbp, const char *file)
1060{
1061 struct timeval times[2];
1062 struct stat sb;
1063
1064 /*
1065 * If we have no info on the input, give this file some
1066 * default values and return..
1067 */
1068 if (sbp == NULL) {
1069 mode_t mask = umask(022);
1070
1071 (void)fchmod(fd, DEFFILEMODE & ~mask);
1072 (void)umask(mask);
1073 return;
1074 }
1075 sb = *sbp;
1076
1077 /* if the chown fails, remove set-id bits as-per compress(1) */
1078 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1079 if (errno != EPERM)
1080 maybe_warn("couldn't fchown: %s", file);
1081 sb.st_mode &= ~(S_ISUID|S_ISGID);
1082 }
1083
1084 /* we only allow set-id and the 9 normal permission bits */
1085 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1086 if (fchmod(fd, sb.st_mode) < 0)
1087 maybe_warn("couldn't fchmod: %s", file);
1088
1089 TIMESPEC_TO_TIMEVAL(&times[0], &sb.st_atim);
1090 TIMESPEC_TO_TIMEVAL(&times[1], &sb.st_mtim);
1091 if (futimes(fd, times) < 0)
1092 maybe_warn("couldn't utimes: %s", file);
1093
1094 /* only try flags if they exist already */
1095 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1096 maybe_warn("couldn't fchflags: %s", file);
1097}
1098#endif
1099
1100/* what sort of file is this? */
1101static enum filetype
1102file_gettype(u_char *buf)
1103{
1104
1105 if (buf[0] == GZIP_MAGIC0 &&
1106 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1107 return FT_GZIP;
1108 else
1109#ifndef NO_BZIP2_SUPPORT
1110 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1111 buf[3] >= '0' && buf[3] <= '9')
1112 return FT_BZIP2;
1113 else
1114#endif
1115#ifndef NO_COMPRESS_SUPPORT
1116 if (memcmp(buf, Z_MAGIC, 2) == 0)
1117 return FT_Z;
1118 else
1119#endif
1120#ifndef NO_PACK_SUPPORT
1121 if (memcmp(buf, PACK_MAGIC, 2) == 0)
1122 return FT_PACK;
1123 else
1124#endif
472/* ... without an errno. */
473void
474maybe_errx(const char *fmt, ...)
475{
476 va_list ap;
477
478 if (qflag == 0) {
479 va_start(ap, fmt);
480 vwarnx(fmt, ap);
481 va_end(ap);
482 }
483 exit(2);
484}
485#endif
486
487#ifndef SMALL
488/* split up $GZIP and prepend it to the argument list */
489static void
490prepend_gzip(char *gzip, int *argc, char ***argv)
491{
492 char *s, **nargv, **ac;
493 int nenvarg = 0, i;
494
495 /* scan how many arguments there are */
496 for (s = gzip;;) {
497 while (*s == ' ' || *s == '\t')
498 s++;
499 if (*s == 0)
500 goto count_done;
501 nenvarg++;
502 while (*s != ' ' && *s != '\t')
503 if (*s++ == 0)
504 goto count_done;
505 }
506count_done:
507 /* punt early */
508 if (nenvarg == 0)
509 return;
510
511 *argc += nenvarg;
512 ac = *argv;
513
514 nargv = (char **)malloc((*argc + 1) * sizeof(char *));
515 if (nargv == NULL)
516 maybe_err("malloc");
517
518 /* stash this away */
519 *argv = nargv;
520
521 /* copy the program name first */
522 i = 0;
523 nargv[i++] = *(ac++);
524
525 /* take a copy of $GZIP and add it to the array */
526 s = strdup(gzip);
527 if (s == NULL)
528 maybe_err("strdup");
529 for (;;) {
530 /* Skip whitespaces. */
531 while (*s == ' ' || *s == '\t')
532 s++;
533 if (*s == 0)
534 goto copy_done;
535 nargv[i++] = s;
536 /* Find the end of this argument. */
537 while (*s != ' ' && *s != '\t')
538 if (*s++ == 0)
539 /* Argument followed by NUL. */
540 goto copy_done;
541 /* Terminate by overwriting ' ' or '\t' with NUL. */
542 *s++ = 0;
543 }
544copy_done:
545
546 /* copy the original arguments and a NULL */
547 while (*ac)
548 nargv[i++] = *(ac++);
549 nargv[i] = NULL;
550}
551#endif
552
553/* compress input to output. Return bytes read, -1 on error */
554static off_t
555gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
556{
557 z_stream z;
558 char *outbufp, *inbufp;
559 off_t in_tot = 0, out_tot = 0;
560 ssize_t in_size;
561 int i, error;
562 uLong crc;
563#ifdef SMALL
564 static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0,
565 0, 0, 0, 0,
566 0, OS_CODE };
567#endif
568
569 outbufp = malloc(BUFLEN);
570 inbufp = malloc(BUFLEN);
571 if (outbufp == NULL || inbufp == NULL) {
572 maybe_err("malloc failed");
573 goto out;
574 }
575
576 memset(&z, 0, sizeof z);
577 z.zalloc = Z_NULL;
578 z.zfree = Z_NULL;
579 z.opaque = 0;
580
581#ifdef SMALL
582 memcpy(outbufp, header, sizeof header);
583 i = sizeof header;
584#else
585 if (nflag != 0) {
586 mtime = 0;
587 origname = "";
588 }
589
590 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s",
591 GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
592 *origname ? ORIG_NAME : 0,
593 mtime & 0xff,
594 (mtime >> 8) & 0xff,
595 (mtime >> 16) & 0xff,
596 (mtime >> 24) & 0xff,
597 numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
598 OS_CODE, origname);
599 if (i >= BUFLEN)
600 /* this need PATH_MAX > BUFLEN ... */
601 maybe_err("snprintf");
602 if (*origname)
603 i++;
604#endif
605
606 z.next_out = (unsigned char *)outbufp + i;
607 z.avail_out = BUFLEN - i;
608
609 error = deflateInit2(&z, numflag, Z_DEFLATED,
610 (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
611 if (error != Z_OK) {
612 maybe_warnx("deflateInit2 failed");
613 in_tot = -1;
614 goto out;
615 }
616
617 crc = crc32(0L, Z_NULL, 0);
618 for (;;) {
619 if (z.avail_out == 0) {
620 if (write(out, outbufp, BUFLEN) != BUFLEN) {
621 maybe_warn("write");
622 out_tot = -1;
623 goto out;
624 }
625
626 out_tot += BUFLEN;
627 z.next_out = (unsigned char *)outbufp;
628 z.avail_out = BUFLEN;
629 }
630
631 if (z.avail_in == 0) {
632 in_size = read(in, inbufp, BUFLEN);
633 if (in_size < 0) {
634 maybe_warn("read");
635 in_tot = -1;
636 goto out;
637 }
638 if (in_size == 0)
639 break;
640
641 crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
642 in_tot += in_size;
643 z.next_in = (unsigned char *)inbufp;
644 z.avail_in = in_size;
645 }
646
647 error = deflate(&z, Z_NO_FLUSH);
648 if (error != Z_OK && error != Z_STREAM_END) {
649 maybe_warnx("deflate failed");
650 in_tot = -1;
651 goto out;
652 }
653 }
654
655 /* clean up */
656 for (;;) {
657 size_t len;
658 ssize_t w;
659
660 error = deflate(&z, Z_FINISH);
661 if (error != Z_OK && error != Z_STREAM_END) {
662 maybe_warnx("deflate failed");
663 in_tot = -1;
664 goto out;
665 }
666
667 len = (char *)z.next_out - outbufp;
668
669 w = write(out, outbufp, len);
670 if (w == -1 || (size_t)w != len) {
671 maybe_warn("write");
672 out_tot = -1;
673 goto out;
674 }
675 out_tot += len;
676 z.next_out = (unsigned char *)outbufp;
677 z.avail_out = BUFLEN;
678
679 if (error == Z_STREAM_END)
680 break;
681 }
682
683 if (deflateEnd(&z) != Z_OK) {
684 maybe_warnx("deflateEnd failed");
685 in_tot = -1;
686 goto out;
687 }
688
689 i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c",
690 (int)crc & 0xff,
691 (int)(crc >> 8) & 0xff,
692 (int)(crc >> 16) & 0xff,
693 (int)(crc >> 24) & 0xff,
694 (int)in_tot & 0xff,
695 (int)(in_tot >> 8) & 0xff,
696 (int)(in_tot >> 16) & 0xff,
697 (int)(in_tot >> 24) & 0xff);
698 if (i != 8)
699 maybe_err("snprintf");
700 if (write(out, outbufp, i) != i) {
701 maybe_warn("write");
702 in_tot = -1;
703 } else
704 out_tot += i;
705
706out:
707 if (inbufp != NULL)
708 free(inbufp);
709 if (outbufp != NULL)
710 free(outbufp);
711 if (gsizep)
712 *gsizep = out_tot;
713 return in_tot;
714}
715
716/*
717 * uncompress input to output then close the input. return the
718 * uncompressed size written, and put the compressed sized read
719 * into `*gsizep'.
720 */
721static off_t
722gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep,
723 const char *filename)
724{
725 z_stream z;
726 char *outbufp, *inbufp;
727 off_t out_tot = -1, in_tot = 0;
728 uint32_t out_sub_tot = 0;
729 enum {
730 GZSTATE_MAGIC0,
731 GZSTATE_MAGIC1,
732 GZSTATE_METHOD,
733 GZSTATE_FLAGS,
734 GZSTATE_SKIPPING,
735 GZSTATE_EXTRA,
736 GZSTATE_EXTRA2,
737 GZSTATE_EXTRA3,
738 GZSTATE_ORIGNAME,
739 GZSTATE_COMMENT,
740 GZSTATE_HEAD_CRC1,
741 GZSTATE_HEAD_CRC2,
742 GZSTATE_INIT,
743 GZSTATE_READ,
744 GZSTATE_CRC,
745 GZSTATE_LEN,
746 } state = GZSTATE_MAGIC0;
747 int flags = 0, skip_count = 0;
748 int error = Z_STREAM_ERROR, done_reading = 0;
749 uLong crc = 0;
750 ssize_t wr;
751 int needmore = 0;
752
753#define ADVANCE() { z.next_in++; z.avail_in--; }
754
755 if ((outbufp = malloc(BUFLEN)) == NULL) {
756 maybe_err("malloc failed");
757 goto out2;
758 }
759 if ((inbufp = malloc(BUFLEN)) == NULL) {
760 maybe_err("malloc failed");
761 goto out1;
762 }
763
764 memset(&z, 0, sizeof z);
765 z.avail_in = prelen;
766 z.next_in = (unsigned char *)pre;
767 z.avail_out = BUFLEN;
768 z.next_out = (unsigned char *)outbufp;
769 z.zalloc = NULL;
770 z.zfree = NULL;
771 z.opaque = 0;
772
773 in_tot = prelen;
774 out_tot = 0;
775
776 for (;;) {
777 if ((z.avail_in == 0 || needmore) && done_reading == 0) {
778 ssize_t in_size;
779
780 if (z.avail_in > 0) {
781 memmove(inbufp, z.next_in, z.avail_in);
782 }
783 z.next_in = (unsigned char *)inbufp;
784 in_size = read(in, z.next_in + z.avail_in,
785 BUFLEN - z.avail_in);
786
787 if (in_size == -1) {
788 maybe_warn("failed to read stdin");
789 goto stop_and_fail;
790 } else if (in_size == 0) {
791 done_reading = 1;
792 }
793
794 z.avail_in += in_size;
795 needmore = 0;
796
797 in_tot += in_size;
798 }
799 if (z.avail_in == 0) {
800 if (done_reading && state != GZSTATE_MAGIC0) {
801 maybe_warnx("%s: unexpected end of file",
802 filename);
803 goto stop_and_fail;
804 }
805 goto stop;
806 }
807 switch (state) {
808 case GZSTATE_MAGIC0:
809 if (*z.next_in != GZIP_MAGIC0) {
810 if (in_tot > 0) {
811 maybe_warnx("%s: trailing garbage "
812 "ignored", filename);
813 goto stop;
814 }
815 maybe_warnx("input not gziped (MAGIC0)");
816 goto stop_and_fail;
817 }
818 ADVANCE();
819 state++;
820 out_sub_tot = 0;
821 crc = crc32(0L, Z_NULL, 0);
822 break;
823
824 case GZSTATE_MAGIC1:
825 if (*z.next_in != GZIP_MAGIC1 &&
826 *z.next_in != GZIP_OMAGIC1) {
827 maybe_warnx("input not gziped (MAGIC1)");
828 goto stop_and_fail;
829 }
830 ADVANCE();
831 state++;
832 break;
833
834 case GZSTATE_METHOD:
835 if (*z.next_in != Z_DEFLATED) {
836 maybe_warnx("unknown compression method");
837 goto stop_and_fail;
838 }
839 ADVANCE();
840 state++;
841 break;
842
843 case GZSTATE_FLAGS:
844 flags = *z.next_in;
845 ADVANCE();
846 skip_count = 6;
847 state++;
848 break;
849
850 case GZSTATE_SKIPPING:
851 if (skip_count > 0) {
852 skip_count--;
853 ADVANCE();
854 } else
855 state++;
856 break;
857
858 case GZSTATE_EXTRA:
859 if ((flags & EXTRA_FIELD) == 0) {
860 state = GZSTATE_ORIGNAME;
861 break;
862 }
863 skip_count = *z.next_in;
864 ADVANCE();
865 state++;
866 break;
867
868 case GZSTATE_EXTRA2:
869 skip_count |= ((*z.next_in) << 8);
870 ADVANCE();
871 state++;
872 break;
873
874 case GZSTATE_EXTRA3:
875 if (skip_count > 0) {
876 skip_count--;
877 ADVANCE();
878 } else
879 state++;
880 break;
881
882 case GZSTATE_ORIGNAME:
883 if ((flags & ORIG_NAME) == 0) {
884 state++;
885 break;
886 }
887 if (*z.next_in == 0)
888 state++;
889 ADVANCE();
890 break;
891
892 case GZSTATE_COMMENT:
893 if ((flags & COMMENT) == 0) {
894 state++;
895 break;
896 }
897 if (*z.next_in == 0)
898 state++;
899 ADVANCE();
900 break;
901
902 case GZSTATE_HEAD_CRC1:
903 if (flags & HEAD_CRC)
904 skip_count = 2;
905 else
906 skip_count = 0;
907 state++;
908 break;
909
910 case GZSTATE_HEAD_CRC2:
911 if (skip_count > 0) {
912 skip_count--;
913 ADVANCE();
914 } else
915 state++;
916 break;
917
918 case GZSTATE_INIT:
919 if (inflateInit2(&z, -MAX_WBITS) != Z_OK) {
920 maybe_warnx("failed to inflateInit");
921 goto stop_and_fail;
922 }
923 state++;
924 break;
925
926 case GZSTATE_READ:
927 error = inflate(&z, Z_FINISH);
928 switch (error) {
929 /* Z_BUF_ERROR goes with Z_FINISH... */
930 case Z_BUF_ERROR:
931 if (z.avail_out > 0 && !done_reading)
932 continue;
933
934 case Z_STREAM_END:
935 case Z_OK:
936 break;
937
938 case Z_NEED_DICT:
939 maybe_warnx("Z_NEED_DICT error");
940 goto stop_and_fail;
941 case Z_DATA_ERROR:
942 maybe_warnx("data stream error");
943 goto stop_and_fail;
944 case Z_STREAM_ERROR:
945 maybe_warnx("internal stream error");
946 goto stop_and_fail;
947 case Z_MEM_ERROR:
948 maybe_warnx("memory allocation error");
949 goto stop_and_fail;
950
951 default:
952 maybe_warn("unknown error from inflate(): %d",
953 error);
954 }
955 wr = BUFLEN - z.avail_out;
956
957 if (wr != 0) {
958 crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr);
959 if (
960#ifndef SMALL
961 /* don't write anything with -t */
962 tflag == 0 &&
963#endif
964 write(out, outbufp, wr) != wr) {
965 maybe_warn("error writing to output");
966 goto stop_and_fail;
967 }
968
969 out_tot += wr;
970 out_sub_tot += wr;
971 }
972
973 if (error == Z_STREAM_END) {
974 inflateEnd(&z);
975 state++;
976 }
977
978 z.next_out = (unsigned char *)outbufp;
979 z.avail_out = BUFLEN;
980
981 break;
982 case GZSTATE_CRC:
983 {
984 uLong origcrc;
985
986 if (z.avail_in < 4) {
987 if (!done_reading) {
988 needmore = 1;
989 continue;
990 }
991 maybe_warnx("truncated input");
992 goto stop_and_fail;
993 }
994 origcrc = ((unsigned)z.next_in[0] & 0xff) |
995 ((unsigned)z.next_in[1] & 0xff) << 8 |
996 ((unsigned)z.next_in[2] & 0xff) << 16 |
997 ((unsigned)z.next_in[3] & 0xff) << 24;
998 if (origcrc != crc) {
999 maybe_warnx("invalid compressed"
1000 " data--crc error");
1001 goto stop_and_fail;
1002 }
1003 }
1004
1005 z.avail_in -= 4;
1006 z.next_in += 4;
1007
1008 if (!z.avail_in && done_reading) {
1009 goto stop;
1010 }
1011 state++;
1012 break;
1013 case GZSTATE_LEN:
1014 {
1015 uLong origlen;
1016
1017 if (z.avail_in < 4) {
1018 if (!done_reading) {
1019 needmore = 1;
1020 continue;
1021 }
1022 maybe_warnx("truncated input");
1023 goto stop_and_fail;
1024 }
1025 origlen = ((unsigned)z.next_in[0] & 0xff) |
1026 ((unsigned)z.next_in[1] & 0xff) << 8 |
1027 ((unsigned)z.next_in[2] & 0xff) << 16 |
1028 ((unsigned)z.next_in[3] & 0xff) << 24;
1029
1030 if (origlen != out_sub_tot) {
1031 maybe_warnx("invalid compressed"
1032 " data--length error");
1033 goto stop_and_fail;
1034 }
1035 }
1036
1037 z.avail_in -= 4;
1038 z.next_in += 4;
1039
1040 if (error < 0) {
1041 maybe_warnx("decompression error");
1042 goto stop_and_fail;
1043 }
1044 state = GZSTATE_MAGIC0;
1045 break;
1046 }
1047 continue;
1048stop_and_fail:
1049 out_tot = -1;
1050stop:
1051 break;
1052 }
1053 if (state > GZSTATE_INIT)
1054 inflateEnd(&z);
1055
1056 free(inbufp);
1057out1:
1058 free(outbufp);
1059out2:
1060 if (gsizep)
1061 *gsizep = in_tot;
1062 return (out_tot);
1063}
1064
1065#ifndef SMALL
1066/*
1067 * set the owner, mode, flags & utimes using the given file descriptor.
1068 * file is only used in possible warning messages.
1069 */
1070static void
1071copymodes(int fd, const struct stat *sbp, const char *file)
1072{
1073 struct timeval times[2];
1074 struct stat sb;
1075
1076 /*
1077 * If we have no info on the input, give this file some
1078 * default values and return..
1079 */
1080 if (sbp == NULL) {
1081 mode_t mask = umask(022);
1082
1083 (void)fchmod(fd, DEFFILEMODE & ~mask);
1084 (void)umask(mask);
1085 return;
1086 }
1087 sb = *sbp;
1088
1089 /* if the chown fails, remove set-id bits as-per compress(1) */
1090 if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
1091 if (errno != EPERM)
1092 maybe_warn("couldn't fchown: %s", file);
1093 sb.st_mode &= ~(S_ISUID|S_ISGID);
1094 }
1095
1096 /* we only allow set-id and the 9 normal permission bits */
1097 sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
1098 if (fchmod(fd, sb.st_mode) < 0)
1099 maybe_warn("couldn't fchmod: %s", file);
1100
1101 TIMESPEC_TO_TIMEVAL(&times[0], &sb.st_atim);
1102 TIMESPEC_TO_TIMEVAL(&times[1], &sb.st_mtim);
1103 if (futimes(fd, times) < 0)
1104 maybe_warn("couldn't utimes: %s", file);
1105
1106 /* only try flags if they exist already */
1107 if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0)
1108 maybe_warn("couldn't fchflags: %s", file);
1109}
1110#endif
1111
1112/* what sort of file is this? */
1113static enum filetype
1114file_gettype(u_char *buf)
1115{
1116
1117 if (buf[0] == GZIP_MAGIC0 &&
1118 (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
1119 return FT_GZIP;
1120 else
1121#ifndef NO_BZIP2_SUPPORT
1122 if (memcmp(buf, BZIP2_MAGIC, 3) == 0 &&
1123 buf[3] >= '0' && buf[3] <= '9')
1124 return FT_BZIP2;
1125 else
1126#endif
1127#ifndef NO_COMPRESS_SUPPORT
1128 if (memcmp(buf, Z_MAGIC, 2) == 0)
1129 return FT_Z;
1130 else
1131#endif
1132#ifndef NO_PACK_SUPPORT
1133 if (memcmp(buf, PACK_MAGIC, 2) == 0)
1134 return FT_PACK;
1135 else
1136#endif
1137#ifndef NO_XZ_SUPPORT
1138 if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */
1139 return FT_XZ;
1140 else
1141#endif
1125 return FT_UNKNOWN;
1126}
1127
1128#ifndef SMALL
1129/* check the outfile is OK. */
1130static int
1131check_outfile(const char *outfile)
1132{
1133 struct stat sb;
1134 int ok = 1;
1135
1136 if (lflag == 0 && stat(outfile, &sb) == 0) {
1137 if (fflag)
1138 unlink(outfile);
1139 else if (isatty(STDIN_FILENO)) {
1140 char ans[10] = { 'n', '\0' }; /* default */
1141
1142 fprintf(stderr, "%s already exists -- do you wish to "
1143 "overwrite (y or n)? " , outfile);
1144 (void)fgets(ans, sizeof(ans) - 1, stdin);
1145 if (ans[0] != 'y' && ans[0] != 'Y') {
1146 fprintf(stderr, "\tnot overwriting\n");
1147 ok = 0;
1148 } else
1149 unlink(outfile);
1150 } else {
1151 maybe_warnx("%s already exists -- skipping", outfile);
1152 ok = 0;
1153 }
1154 }
1155 return ok;
1156}
1157
1158static void
1159unlink_input(const char *file, const struct stat *sb)
1160{
1161 struct stat nsb;
1162
1163 if (kflag)
1164 return;
1165 if (stat(file, &nsb) != 0)
1166 /* Must be gone already */
1167 return;
1168 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1169 /* Definitely a different file */
1170 return;
1171 unlink(file);
1172}
1173
1174static void
1175sigint_handler(int signo __unused)
1176{
1177
1178 if (remove_file != NULL)
1179 unlink(remove_file);
1180 _exit(2);
1181}
1182#endif
1183
1184static const suffixes_t *
1185check_suffix(char *file, int xlate)
1186{
1187 const suffixes_t *s;
1188 int len = strlen(file);
1189 char *sp;
1190
1191 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1192 /* if it doesn't fit in "a.suf", don't bother */
1193 if (s->ziplen >= len)
1194 continue;
1195 sp = file + len - s->ziplen;
1196 if (strcmp(s->zipped, sp) != 0)
1197 continue;
1198 if (xlate)
1199 strcpy(sp, s->normal);
1200 return s;
1201 }
1202 return NULL;
1203}
1204
1205/*
1206 * compress the given file: create a corresponding .gz file and remove the
1207 * original.
1208 */
1209static off_t
1210file_compress(char *file, char *outfile, size_t outsize)
1211{
1212 int in;
1213 int out;
1214 off_t size, insize;
1215#ifndef SMALL
1216 struct stat isb, osb;
1217 const suffixes_t *suff;
1218#endif
1219
1220 in = open(file, O_RDONLY);
1221 if (in == -1) {
1222 maybe_warn("can't open %s", file);
1223 return (-1);
1224 }
1225
1226#ifndef SMALL
1227 if (fstat(in, &isb) != 0) {
1228 maybe_warn("couldn't stat: %s", file);
1229 close(in);
1230 return (-1);
1231 }
1232#endif
1233
1234 if (cflag == 0) {
1235#ifndef SMALL
1236 if (isb.st_nlink > 1 && fflag == 0) {
1237 maybe_warnx("%s has %d other link%s -- skipping",
1238 file, isb.st_nlink - 1,
1239 (isb.st_nlink - 1) == 1 ? "" : "s");
1240 close(in);
1241 return (-1);
1242 }
1243
1244 if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1245 suff->zipped[0] != 0) {
1246 maybe_warnx("%s already has %s suffix -- unchanged",
1247 file, suff->zipped);
1248 close(in);
1249 return (-1);
1250 }
1251#endif
1252
1253 /* Add (usually) .gz to filename */
1254 if ((size_t)snprintf(outfile, outsize, "%s%s",
1255 file, suffixes[0].zipped) >= outsize)
1256 memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1257 suffixes[0].zipped, suffixes[0].ziplen + 1);
1258
1259#ifndef SMALL
1260 if (check_outfile(outfile) == 0) {
1261 close(in);
1262 return (-1);
1263 }
1264#endif
1265 }
1266
1267 if (cflag == 0) {
1268 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1269 if (out == -1) {
1270 maybe_warn("could not create output: %s", outfile);
1271 fclose(stdin);
1272 return (-1);
1273 }
1274#ifndef SMALL
1275 remove_file = outfile;
1276#endif
1277 } else
1278 out = STDOUT_FILENO;
1279
1280 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1281
1282 (void)close(in);
1283
1284 /*
1285 * If there was an error, insize will be -1.
1286 * If we compressed to stdout, just return the size.
1287 * Otherwise stat the file and check it is the correct size.
1288 * We only blow away the file if we can stat the output and it
1289 * has the expected size.
1290 */
1291 if (cflag != 0)
1292 return (insize == -1 ? -1 : size);
1293
1294#ifndef SMALL
1295 if (fstat(out, &osb) != 0) {
1296 maybe_warn("couldn't stat: %s", outfile);
1297 goto bad_outfile;
1298 }
1299
1300 if (osb.st_size != size) {
1301 maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1302 outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1303 goto bad_outfile;
1304 }
1305
1306 copymodes(out, &isb, outfile);
1307 remove_file = NULL;
1308#endif
1309 if (close(out) == -1)
1310 maybe_warn("couldn't close output");
1311
1312 /* output is good, ok to delete input */
1313 unlink_input(file, &isb);
1314 return (size);
1315
1316#ifndef SMALL
1317 bad_outfile:
1318 if (close(out) == -1)
1319 maybe_warn("couldn't close output");
1320
1321 maybe_warnx("leaving original %s", file);
1322 unlink(outfile);
1323 return (size);
1324#endif
1325}
1326
1327/* uncompress the given file and remove the original */
1328static off_t
1329file_uncompress(char *file, char *outfile, size_t outsize)
1330{
1331 struct stat isb, osb;
1332 off_t size;
1333 ssize_t rbytes;
1334 unsigned char header1[4];
1335 enum filetype method;
1336 int fd, ofd, zfd = -1;
1337#ifndef SMALL
1338 ssize_t rv;
1339 time_t timestamp = 0;
1340 unsigned char name[PATH_MAX + 1];
1341#endif
1342
1343 /* gather the old name info */
1344
1345 fd = open(file, O_RDONLY);
1346 if (fd < 0) {
1347 maybe_warn("can't open %s", file);
1348 goto lose;
1349 }
1350
1351 strlcpy(outfile, file, outsize);
1352 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1353 maybe_warnx("%s: unknown suffix -- ignored", file);
1354 goto lose;
1355 }
1356
1357 rbytes = read(fd, header1, sizeof header1);
1358 if (rbytes != sizeof header1) {
1359 /* we don't want to fail here. */
1360#ifndef SMALL
1361 if (fflag)
1362 goto lose;
1363#endif
1364 if (rbytes == -1)
1365 maybe_warn("can't read %s", file);
1366 else
1367 goto unexpected_EOF;
1368 goto lose;
1369 }
1370
1371 method = file_gettype(header1);
1142 return FT_UNKNOWN;
1143}
1144
1145#ifndef SMALL
1146/* check the outfile is OK. */
1147static int
1148check_outfile(const char *outfile)
1149{
1150 struct stat sb;
1151 int ok = 1;
1152
1153 if (lflag == 0 && stat(outfile, &sb) == 0) {
1154 if (fflag)
1155 unlink(outfile);
1156 else if (isatty(STDIN_FILENO)) {
1157 char ans[10] = { 'n', '\0' }; /* default */
1158
1159 fprintf(stderr, "%s already exists -- do you wish to "
1160 "overwrite (y or n)? " , outfile);
1161 (void)fgets(ans, sizeof(ans) - 1, stdin);
1162 if (ans[0] != 'y' && ans[0] != 'Y') {
1163 fprintf(stderr, "\tnot overwriting\n");
1164 ok = 0;
1165 } else
1166 unlink(outfile);
1167 } else {
1168 maybe_warnx("%s already exists -- skipping", outfile);
1169 ok = 0;
1170 }
1171 }
1172 return ok;
1173}
1174
1175static void
1176unlink_input(const char *file, const struct stat *sb)
1177{
1178 struct stat nsb;
1179
1180 if (kflag)
1181 return;
1182 if (stat(file, &nsb) != 0)
1183 /* Must be gone already */
1184 return;
1185 if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
1186 /* Definitely a different file */
1187 return;
1188 unlink(file);
1189}
1190
1191static void
1192sigint_handler(int signo __unused)
1193{
1194
1195 if (remove_file != NULL)
1196 unlink(remove_file);
1197 _exit(2);
1198}
1199#endif
1200
1201static const suffixes_t *
1202check_suffix(char *file, int xlate)
1203{
1204 const suffixes_t *s;
1205 int len = strlen(file);
1206 char *sp;
1207
1208 for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
1209 /* if it doesn't fit in "a.suf", don't bother */
1210 if (s->ziplen >= len)
1211 continue;
1212 sp = file + len - s->ziplen;
1213 if (strcmp(s->zipped, sp) != 0)
1214 continue;
1215 if (xlate)
1216 strcpy(sp, s->normal);
1217 return s;
1218 }
1219 return NULL;
1220}
1221
1222/*
1223 * compress the given file: create a corresponding .gz file and remove the
1224 * original.
1225 */
1226static off_t
1227file_compress(char *file, char *outfile, size_t outsize)
1228{
1229 int in;
1230 int out;
1231 off_t size, insize;
1232#ifndef SMALL
1233 struct stat isb, osb;
1234 const suffixes_t *suff;
1235#endif
1236
1237 in = open(file, O_RDONLY);
1238 if (in == -1) {
1239 maybe_warn("can't open %s", file);
1240 return (-1);
1241 }
1242
1243#ifndef SMALL
1244 if (fstat(in, &isb) != 0) {
1245 maybe_warn("couldn't stat: %s", file);
1246 close(in);
1247 return (-1);
1248 }
1249#endif
1250
1251 if (cflag == 0) {
1252#ifndef SMALL
1253 if (isb.st_nlink > 1 && fflag == 0) {
1254 maybe_warnx("%s has %d other link%s -- skipping",
1255 file, isb.st_nlink - 1,
1256 (isb.st_nlink - 1) == 1 ? "" : "s");
1257 close(in);
1258 return (-1);
1259 }
1260
1261 if (fflag == 0 && (suff = check_suffix(file, 0)) &&
1262 suff->zipped[0] != 0) {
1263 maybe_warnx("%s already has %s suffix -- unchanged",
1264 file, suff->zipped);
1265 close(in);
1266 return (-1);
1267 }
1268#endif
1269
1270 /* Add (usually) .gz to filename */
1271 if ((size_t)snprintf(outfile, outsize, "%s%s",
1272 file, suffixes[0].zipped) >= outsize)
1273 memcpy(outfile + outsize - suffixes[0].ziplen - 1,
1274 suffixes[0].zipped, suffixes[0].ziplen + 1);
1275
1276#ifndef SMALL
1277 if (check_outfile(outfile) == 0) {
1278 close(in);
1279 return (-1);
1280 }
1281#endif
1282 }
1283
1284 if (cflag == 0) {
1285 out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
1286 if (out == -1) {
1287 maybe_warn("could not create output: %s", outfile);
1288 fclose(stdin);
1289 return (-1);
1290 }
1291#ifndef SMALL
1292 remove_file = outfile;
1293#endif
1294 } else
1295 out = STDOUT_FILENO;
1296
1297 insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime);
1298
1299 (void)close(in);
1300
1301 /*
1302 * If there was an error, insize will be -1.
1303 * If we compressed to stdout, just return the size.
1304 * Otherwise stat the file and check it is the correct size.
1305 * We only blow away the file if we can stat the output and it
1306 * has the expected size.
1307 */
1308 if (cflag != 0)
1309 return (insize == -1 ? -1 : size);
1310
1311#ifndef SMALL
1312 if (fstat(out, &osb) != 0) {
1313 maybe_warn("couldn't stat: %s", outfile);
1314 goto bad_outfile;
1315 }
1316
1317 if (osb.st_size != size) {
1318 maybe_warnx("output file: %s wrong size (%ju != %ju), deleting",
1319 outfile, (uintmax_t)osb.st_size, (uintmax_t)size);
1320 goto bad_outfile;
1321 }
1322
1323 copymodes(out, &isb, outfile);
1324 remove_file = NULL;
1325#endif
1326 if (close(out) == -1)
1327 maybe_warn("couldn't close output");
1328
1329 /* output is good, ok to delete input */
1330 unlink_input(file, &isb);
1331 return (size);
1332
1333#ifndef SMALL
1334 bad_outfile:
1335 if (close(out) == -1)
1336 maybe_warn("couldn't close output");
1337
1338 maybe_warnx("leaving original %s", file);
1339 unlink(outfile);
1340 return (size);
1341#endif
1342}
1343
1344/* uncompress the given file and remove the original */
1345static off_t
1346file_uncompress(char *file, char *outfile, size_t outsize)
1347{
1348 struct stat isb, osb;
1349 off_t size;
1350 ssize_t rbytes;
1351 unsigned char header1[4];
1352 enum filetype method;
1353 int fd, ofd, zfd = -1;
1354#ifndef SMALL
1355 ssize_t rv;
1356 time_t timestamp = 0;
1357 unsigned char name[PATH_MAX + 1];
1358#endif
1359
1360 /* gather the old name info */
1361
1362 fd = open(file, O_RDONLY);
1363 if (fd < 0) {
1364 maybe_warn("can't open %s", file);
1365 goto lose;
1366 }
1367
1368 strlcpy(outfile, file, outsize);
1369 if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) {
1370 maybe_warnx("%s: unknown suffix -- ignored", file);
1371 goto lose;
1372 }
1373
1374 rbytes = read(fd, header1, sizeof header1);
1375 if (rbytes != sizeof header1) {
1376 /* we don't want to fail here. */
1377#ifndef SMALL
1378 if (fflag)
1379 goto lose;
1380#endif
1381 if (rbytes == -1)
1382 maybe_warn("can't read %s", file);
1383 else
1384 goto unexpected_EOF;
1385 goto lose;
1386 }
1387
1388 method = file_gettype(header1);
1372
1373#ifndef SMALL
1374 if (fflag == 0 && method == FT_UNKNOWN) {
1375 maybe_warnx("%s: not in gzip format", file);
1376 goto lose;
1377 }
1378
1379#endif
1380
1381#ifndef SMALL
1382 if (method == FT_GZIP && Nflag) {
1383 unsigned char ts[4]; /* timestamp */
1384
1385 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1386 if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1387 goto unexpected_EOF;
1388 if (rv == -1) {
1389 if (!fflag)
1390 maybe_warn("can't read %s", file);
1391 goto lose;
1392 }
1393 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1394
1395 if (header1[3] & ORIG_NAME) {
1396 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1397 if (rbytes < 0) {
1398 maybe_warn("can't read %s", file);
1399 goto lose;
1400 }
1401 if (name[0] != 0) {
1402 /* preserve original directory name */
1403 char *dp = strrchr(file, '/');
1404 if (dp == NULL)
1405 dp = file;
1406 else
1407 dp++;
1408 snprintf(outfile, outsize, "%.*s%.*s",
1409 (int) (dp - file),
1410 file, (int) rbytes, name);
1411 }
1412 }
1413 }
1414#endif
1415 lseek(fd, 0, SEEK_SET);
1416
1417 if (cflag == 0 || lflag) {
1418 if (fstat(fd, &isb) != 0)
1419 goto lose;
1420#ifndef SMALL
1421 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1422 maybe_warnx("%s has %d other links -- skipping",
1423 file, isb.st_nlink - 1);
1424 goto lose;
1425 }
1426 if (nflag == 0 && timestamp)
1427 isb.st_mtime = timestamp;
1428 if (check_outfile(outfile) == 0)
1429 goto lose;
1430#endif
1431 }
1432
1433 if (cflag == 0 && lflag == 0) {
1434 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1435 if (zfd == STDOUT_FILENO) {
1436 /* We won't close STDOUT_FILENO later... */
1437 zfd = dup(zfd);
1438 close(STDOUT_FILENO);
1439 }
1440 if (zfd == -1) {
1441 maybe_warn("can't open %s", outfile);
1442 goto lose;
1443 }
1444#ifndef SMALL
1445 remove_file = outfile;
1446#endif
1447 } else
1448 zfd = STDOUT_FILENO;
1449
1389#ifndef SMALL
1390 if (fflag == 0 && method == FT_UNKNOWN) {
1391 maybe_warnx("%s: not in gzip format", file);
1392 goto lose;
1393 }
1394
1395#endif
1396
1397#ifndef SMALL
1398 if (method == FT_GZIP && Nflag) {
1399 unsigned char ts[4]; /* timestamp */
1400
1401 rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP);
1402 if (rv >= 0 && rv < (ssize_t)(sizeof ts))
1403 goto unexpected_EOF;
1404 if (rv == -1) {
1405 if (!fflag)
1406 maybe_warn("can't read %s", file);
1407 goto lose;
1408 }
1409 timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0];
1410
1411 if (header1[3] & ORIG_NAME) {
1412 rbytes = pread(fd, name, sizeof name, GZIP_ORIGNAME);
1413 if (rbytes < 0) {
1414 maybe_warn("can't read %s", file);
1415 goto lose;
1416 }
1417 if (name[0] != 0) {
1418 /* preserve original directory name */
1419 char *dp = strrchr(file, '/');
1420 if (dp == NULL)
1421 dp = file;
1422 else
1423 dp++;
1424 snprintf(outfile, outsize, "%.*s%.*s",
1425 (int) (dp - file),
1426 file, (int) rbytes, name);
1427 }
1428 }
1429 }
1430#endif
1431 lseek(fd, 0, SEEK_SET);
1432
1433 if (cflag == 0 || lflag) {
1434 if (fstat(fd, &isb) != 0)
1435 goto lose;
1436#ifndef SMALL
1437 if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) {
1438 maybe_warnx("%s has %d other links -- skipping",
1439 file, isb.st_nlink - 1);
1440 goto lose;
1441 }
1442 if (nflag == 0 && timestamp)
1443 isb.st_mtime = timestamp;
1444 if (check_outfile(outfile) == 0)
1445 goto lose;
1446#endif
1447 }
1448
1449 if (cflag == 0 && lflag == 0) {
1450 zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
1451 if (zfd == STDOUT_FILENO) {
1452 /* We won't close STDOUT_FILENO later... */
1453 zfd = dup(zfd);
1454 close(STDOUT_FILENO);
1455 }
1456 if (zfd == -1) {
1457 maybe_warn("can't open %s", outfile);
1458 goto lose;
1459 }
1460#ifndef SMALL
1461 remove_file = outfile;
1462#endif
1463 } else
1464 zfd = STDOUT_FILENO;
1465
1466 switch (method) {
1450#ifndef NO_BZIP2_SUPPORT
1467#ifndef NO_BZIP2_SUPPORT
1451 if (method == FT_BZIP2) {
1452
1468 case FT_BZIP2:
1453 /* XXX */
1454 if (lflag) {
1455 maybe_warnx("no -l with bzip2 files");
1456 goto lose;
1457 }
1458
1459 size = unbzip2(fd, zfd, NULL, 0, NULL);
1469 /* XXX */
1470 if (lflag) {
1471 maybe_warnx("no -l with bzip2 files");
1472 goto lose;
1473 }
1474
1475 size = unbzip2(fd, zfd, NULL, 0, NULL);
1460 } else
1476 break;
1461#endif
1462
1463#ifndef NO_COMPRESS_SUPPORT
1477#endif
1478
1479#ifndef NO_COMPRESS_SUPPORT
1464 if (method == FT_Z) {
1480 case FT_Z: {
1465 FILE *in, *out;
1466
1467 /* XXX */
1468 if (lflag) {
1469 maybe_warnx("no -l with Lempel-Ziv files");
1470 goto lose;
1471 }
1472
1473 if ((in = zdopen(fd)) == NULL) {
1474 maybe_warn("zdopen for read: %s", file);
1475 goto lose;
1476 }
1477
1478 out = fdopen(dup(zfd), "w");
1479 if (out == NULL) {
1480 maybe_warn("fdopen for write: %s", outfile);
1481 fclose(in);
1482 goto lose;
1483 }
1484
1485 size = zuncompress(in, out, NULL, 0, NULL);
1486 /* need to fclose() if ferror() is true... */
1487 if (ferror(in) | fclose(in)) {
1488 maybe_warn("failed infile fclose");
1489 unlink(outfile);
1490 (void)fclose(out);
1491 }
1492 if (fclose(out) != 0) {
1493 maybe_warn("failed outfile fclose");
1494 unlink(outfile);
1495 goto lose;
1496 }
1481 FILE *in, *out;
1482
1483 /* XXX */
1484 if (lflag) {
1485 maybe_warnx("no -l with Lempel-Ziv files");
1486 goto lose;
1487 }
1488
1489 if ((in = zdopen(fd)) == NULL) {
1490 maybe_warn("zdopen for read: %s", file);
1491 goto lose;
1492 }
1493
1494 out = fdopen(dup(zfd), "w");
1495 if (out == NULL) {
1496 maybe_warn("fdopen for write: %s", outfile);
1497 fclose(in);
1498 goto lose;
1499 }
1500
1501 size = zuncompress(in, out, NULL, 0, NULL);
1502 /* need to fclose() if ferror() is true... */
1503 if (ferror(in) | fclose(in)) {
1504 maybe_warn("failed infile fclose");
1505 unlink(outfile);
1506 (void)fclose(out);
1507 }
1508 if (fclose(out) != 0) {
1509 maybe_warn("failed outfile fclose");
1510 unlink(outfile);
1511 goto lose;
1512 }
1497 } else
1513 break;
1514 }
1498#endif
1499
1500#ifndef NO_PACK_SUPPORT
1515#endif
1516
1517#ifndef NO_PACK_SUPPORT
1501 if (method == FT_PACK) {
1518 case FT_PACK:
1502 if (lflag) {
1503 maybe_warnx("no -l with packed files");
1504 goto lose;
1505 }
1506
1507 size = unpack(fd, zfd, NULL, 0, NULL);
1519 if (lflag) {
1520 maybe_warnx("no -l with packed files");
1521 goto lose;
1522 }
1523
1524 size = unpack(fd, zfd, NULL, 0, NULL);
1508 } else
1525 break;
1509#endif
1510
1526#endif
1527
1528#ifndef NO_XZ_SUPPORT
1529 case FT_XZ:
1530 if (lflag) {
1531 maybe_warnx("no -l with xz files");
1532 goto lose;
1533 }
1534
1535 size = unxz(fd, zfd, NULL, 0, NULL);
1536 break;
1537#endif
1538
1511#ifndef SMALL
1539#ifndef SMALL
1512 if (method == FT_UNKNOWN) {
1540 case FT_UNKNOWN:
1513 if (lflag) {
1514 maybe_warnx("no -l for unknown filetypes");
1515 goto lose;
1516 }
1517 size = cat_fd(NULL, 0, NULL, fd);
1541 if (lflag) {
1542 maybe_warnx("no -l for unknown filetypes");
1543 goto lose;
1544 }
1545 size = cat_fd(NULL, 0, NULL, fd);
1518 } else
1546 break;
1519#endif
1547#endif
1520 {
1548 default:
1521 if (lflag) {
1522 print_list(fd, isb.st_size, outfile, isb.st_mtime);
1523 close(fd);
1524 return -1; /* XXX */
1525 }
1526
1527 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1549 if (lflag) {
1550 print_list(fd, isb.st_size, outfile, isb.st_mtime);
1551 close(fd);
1552 return -1; /* XXX */
1553 }
1554
1555 size = gz_uncompress(fd, zfd, NULL, 0, NULL, file);
1556 break;
1528 }
1529
1530 if (close(fd) != 0)
1531 maybe_warn("couldn't close input");
1532 if (zfd != STDOUT_FILENO && close(zfd) != 0)
1533 maybe_warn("couldn't close output");
1534
1535 if (size == -1) {
1536 if (cflag == 0)
1537 unlink(outfile);
1538 maybe_warnx("%s: uncompress failed", file);
1539 return -1;
1540 }
1541
1542 /* if testing, or we uncompressed to stdout, this is all we need */
1543#ifndef SMALL
1544 if (tflag)
1545 return size;
1546#endif
1547 /* if we are uncompressing to stdin, don't remove the file. */
1548 if (cflag)
1549 return size;
1550
1551 /*
1552 * if we create a file...
1553 */
1554 /*
1555 * if we can't stat the file don't remove the file.
1556 */
1557
1558 ofd = open(outfile, O_RDWR, 0);
1559 if (ofd == -1) {
1560 maybe_warn("couldn't open (leaving original): %s",
1561 outfile);
1562 return -1;
1563 }
1564 if (fstat(ofd, &osb) != 0) {
1565 maybe_warn("couldn't stat (leaving original): %s",
1566 outfile);
1567 close(ofd);
1568 return -1;
1569 }
1570 if (osb.st_size != size) {
1571 maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1572 (uintmax_t)size, (uintmax_t)osb.st_size);
1573 close(ofd);
1574 unlink(outfile);
1575 return -1;
1576 }
1577#ifndef SMALL
1578 copymodes(ofd, &isb, outfile);
1579 remove_file = NULL;
1580#endif
1581 close(ofd);
1582 unlink_input(file, &isb);
1583 return size;
1584
1585 unexpected_EOF:
1586 maybe_warnx("%s: unexpected end of file", file);
1587 lose:
1588 if (fd != -1)
1589 close(fd);
1590 if (zfd != -1 && zfd != STDOUT_FILENO)
1591 close(fd);
1592 return -1;
1593}
1594
1595#ifndef SMALL
1596static off_t
1597cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1598{
1599 char buf[BUFLEN];
1600 off_t in_tot;
1601 ssize_t w;
1602
1603 in_tot = count;
1604 w = write(STDOUT_FILENO, prepend, count);
1605 if (w == -1 || (size_t)w != count) {
1606 maybe_warn("write to stdout");
1607 return -1;
1608 }
1609 for (;;) {
1610 ssize_t rv;
1611
1612 rv = read(fd, buf, sizeof buf);
1613 if (rv == 0)
1614 break;
1615 if (rv < 0) {
1616 maybe_warn("read from fd %d", fd);
1617 break;
1618 }
1619
1620 if (write(STDOUT_FILENO, buf, rv) != rv) {
1621 maybe_warn("write to stdout");
1622 break;
1623 }
1624 in_tot += rv;
1625 }
1626
1627 if (gsizep)
1628 *gsizep = in_tot;
1629 return (in_tot);
1630}
1631#endif
1632
1633static void
1634handle_stdin(void)
1635{
1636 unsigned char header1[4];
1637 off_t usize, gsize;
1638 enum filetype method;
1639 ssize_t bytes_read;
1640#ifndef NO_COMPRESS_SUPPORT
1641 FILE *in;
1642#endif
1643
1644#ifndef SMALL
1645 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1646 maybe_warnx("standard input is a terminal -- ignoring");
1647 return;
1648 }
1649#endif
1650
1651 if (lflag) {
1652 struct stat isb;
1653
1654 /* XXX could read the whole file, etc. */
1655 if (fstat(STDIN_FILENO, &isb) < 0) {
1656 maybe_warn("fstat");
1657 return;
1658 }
1659 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1660 return;
1661 }
1662
1663 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1664 if (bytes_read == -1) {
1665 maybe_warn("can't read stdin");
1666 return;
1667 } else if (bytes_read != sizeof(header1)) {
1668 maybe_warnx("(stdin): unexpected end of file");
1669 return;
1670 }
1671
1672 method = file_gettype(header1);
1673 switch (method) {
1674 default:
1675#ifndef SMALL
1676 if (fflag == 0) {
1677 maybe_warnx("unknown compression format");
1678 return;
1679 }
1680 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1681 break;
1682#endif
1683 case FT_GZIP:
1684 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1685 (char *)header1, sizeof header1, &gsize, "(stdin)");
1686 break;
1687#ifndef NO_BZIP2_SUPPORT
1688 case FT_BZIP2:
1689 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1690 (char *)header1, sizeof header1, &gsize);
1691 break;
1692#endif
1693#ifndef NO_COMPRESS_SUPPORT
1694 case FT_Z:
1695 if ((in = zdopen(STDIN_FILENO)) == NULL) {
1696 maybe_warnx("zopen of stdin");
1697 return;
1698 }
1699
1557 }
1558
1559 if (close(fd) != 0)
1560 maybe_warn("couldn't close input");
1561 if (zfd != STDOUT_FILENO && close(zfd) != 0)
1562 maybe_warn("couldn't close output");
1563
1564 if (size == -1) {
1565 if (cflag == 0)
1566 unlink(outfile);
1567 maybe_warnx("%s: uncompress failed", file);
1568 return -1;
1569 }
1570
1571 /* if testing, or we uncompressed to stdout, this is all we need */
1572#ifndef SMALL
1573 if (tflag)
1574 return size;
1575#endif
1576 /* if we are uncompressing to stdin, don't remove the file. */
1577 if (cflag)
1578 return size;
1579
1580 /*
1581 * if we create a file...
1582 */
1583 /*
1584 * if we can't stat the file don't remove the file.
1585 */
1586
1587 ofd = open(outfile, O_RDWR, 0);
1588 if (ofd == -1) {
1589 maybe_warn("couldn't open (leaving original): %s",
1590 outfile);
1591 return -1;
1592 }
1593 if (fstat(ofd, &osb) != 0) {
1594 maybe_warn("couldn't stat (leaving original): %s",
1595 outfile);
1596 close(ofd);
1597 return -1;
1598 }
1599 if (osb.st_size != size) {
1600 maybe_warnx("stat gave different size: %ju != %ju (leaving original)",
1601 (uintmax_t)size, (uintmax_t)osb.st_size);
1602 close(ofd);
1603 unlink(outfile);
1604 return -1;
1605 }
1606#ifndef SMALL
1607 copymodes(ofd, &isb, outfile);
1608 remove_file = NULL;
1609#endif
1610 close(ofd);
1611 unlink_input(file, &isb);
1612 return size;
1613
1614 unexpected_EOF:
1615 maybe_warnx("%s: unexpected end of file", file);
1616 lose:
1617 if (fd != -1)
1618 close(fd);
1619 if (zfd != -1 && zfd != STDOUT_FILENO)
1620 close(fd);
1621 return -1;
1622}
1623
1624#ifndef SMALL
1625static off_t
1626cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd)
1627{
1628 char buf[BUFLEN];
1629 off_t in_tot;
1630 ssize_t w;
1631
1632 in_tot = count;
1633 w = write(STDOUT_FILENO, prepend, count);
1634 if (w == -1 || (size_t)w != count) {
1635 maybe_warn("write to stdout");
1636 return -1;
1637 }
1638 for (;;) {
1639 ssize_t rv;
1640
1641 rv = read(fd, buf, sizeof buf);
1642 if (rv == 0)
1643 break;
1644 if (rv < 0) {
1645 maybe_warn("read from fd %d", fd);
1646 break;
1647 }
1648
1649 if (write(STDOUT_FILENO, buf, rv) != rv) {
1650 maybe_warn("write to stdout");
1651 break;
1652 }
1653 in_tot += rv;
1654 }
1655
1656 if (gsizep)
1657 *gsizep = in_tot;
1658 return (in_tot);
1659}
1660#endif
1661
1662static void
1663handle_stdin(void)
1664{
1665 unsigned char header1[4];
1666 off_t usize, gsize;
1667 enum filetype method;
1668 ssize_t bytes_read;
1669#ifndef NO_COMPRESS_SUPPORT
1670 FILE *in;
1671#endif
1672
1673#ifndef SMALL
1674 if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) {
1675 maybe_warnx("standard input is a terminal -- ignoring");
1676 return;
1677 }
1678#endif
1679
1680 if (lflag) {
1681 struct stat isb;
1682
1683 /* XXX could read the whole file, etc. */
1684 if (fstat(STDIN_FILENO, &isb) < 0) {
1685 maybe_warn("fstat");
1686 return;
1687 }
1688 print_list(STDIN_FILENO, isb.st_size, "stdout", isb.st_mtime);
1689 return;
1690 }
1691
1692 bytes_read = read_retry(STDIN_FILENO, header1, sizeof header1);
1693 if (bytes_read == -1) {
1694 maybe_warn("can't read stdin");
1695 return;
1696 } else if (bytes_read != sizeof(header1)) {
1697 maybe_warnx("(stdin): unexpected end of file");
1698 return;
1699 }
1700
1701 method = file_gettype(header1);
1702 switch (method) {
1703 default:
1704#ifndef SMALL
1705 if (fflag == 0) {
1706 maybe_warnx("unknown compression format");
1707 return;
1708 }
1709 usize = cat_fd(header1, sizeof header1, &gsize, STDIN_FILENO);
1710 break;
1711#endif
1712 case FT_GZIP:
1713 usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO,
1714 (char *)header1, sizeof header1, &gsize, "(stdin)");
1715 break;
1716#ifndef NO_BZIP2_SUPPORT
1717 case FT_BZIP2:
1718 usize = unbzip2(STDIN_FILENO, STDOUT_FILENO,
1719 (char *)header1, sizeof header1, &gsize);
1720 break;
1721#endif
1722#ifndef NO_COMPRESS_SUPPORT
1723 case FT_Z:
1724 if ((in = zdopen(STDIN_FILENO)) == NULL) {
1725 maybe_warnx("zopen of stdin");
1726 return;
1727 }
1728
1700 usize = zuncompress(in, stdout, (char *)header1, sizeof header1, &gsize);
1729 usize = zuncompress(in, stdout, (char *)header1,
1730 sizeof header1, &gsize);
1701 fclose(in);
1702 break;
1703#endif
1704#ifndef NO_PACK_SUPPORT
1705 case FT_PACK:
1706 usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1707 (char *)header1, sizeof header1, &gsize);
1708 break;
1709#endif
1731 fclose(in);
1732 break;
1733#endif
1734#ifndef NO_PACK_SUPPORT
1735 case FT_PACK:
1736 usize = unpack(STDIN_FILENO, STDOUT_FILENO,
1737 (char *)header1, sizeof header1, &gsize);
1738 break;
1739#endif
1740#ifndef NO_XZ_SUPPORT
1741 case FT_XZ:
1742 usize = unxz(STDIN_FILENO, STDOUT_FILENO,
1743 (char *)header1, sizeof header1, &gsize);
1744 break;
1745#endif
1710 }
1711
1712#ifndef SMALL
1713 if (vflag && !tflag && usize != -1 && gsize != -1)
1714 print_verbage(NULL, NULL, usize, gsize);
1715 if (vflag && tflag)
1716 print_test("(stdin)", usize != -1);
1717#endif
1718
1719}
1720
1721static void
1722handle_stdout(void)
1723{
1724 off_t gsize, usize;
1725 struct stat sb;
1726 time_t systime;
1727 uint32_t mtime;
1728 int ret;
1729
1730#ifndef SMALL
1731 if (fflag == 0 && isatty(STDOUT_FILENO)) {
1732 maybe_warnx("standard output is a terminal -- ignoring");
1733 return;
1734 }
1735#endif
1736 /* If stdin is a file use it's mtime, otherwise use current time */
1737 ret = fstat(STDIN_FILENO, &sb);
1738
1739#ifndef SMALL
1740 if (ret < 0) {
1741 maybe_warn("Can't stat stdin");
1742 return;
1743 }
1744#endif
1745
1746 if (S_ISREG(sb.st_mode))
1747 mtime = (uint32_t)sb.st_mtime;
1748 else {
1749 systime = time(NULL);
1750#ifndef SMALL
1751 if (systime == -1) {
1752 maybe_warn("time");
1753 return;
1754 }
1755#endif
1756 mtime = (uint32_t)systime;
1757 }
1758
1759 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1760#ifndef SMALL
1761 if (vflag && !tflag && usize != -1 && gsize != -1)
1762 print_verbage(NULL, NULL, usize, gsize);
1763#endif
1764}
1765
1766/* do what is asked for, for the path name */
1767static void
1768handle_pathname(char *path)
1769{
1770 char *opath = path, *s = NULL;
1771 ssize_t len;
1772 int slen;
1773 struct stat sb;
1774
1775 /* check for stdout/stdin */
1776 if (path[0] == '-' && path[1] == '\0') {
1777 if (dflag)
1778 handle_stdin();
1779 else
1780 handle_stdout();
1781 return;
1782 }
1783
1784retry:
1785 if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1786 lstat(path, &sb) != 0)) {
1787 /* lets try <path>.gz if we're decompressing */
1788 if (dflag && s == NULL && errno == ENOENT) {
1789 len = strlen(path);
1790 slen = suffixes[0].ziplen;
1791 s = malloc(len + slen + 1);
1792 if (s == NULL)
1793 maybe_err("malloc");
1794 memcpy(s, path, len);
1795 memcpy(s + len, suffixes[0].zipped, slen + 1);
1796 path = s;
1797 goto retry;
1798 }
1799 maybe_warn("can't stat: %s", opath);
1800 goto out;
1801 }
1802
1803 if (S_ISDIR(sb.st_mode)) {
1804#ifndef SMALL
1805 if (rflag)
1806 handle_dir(path);
1807 else
1808#endif
1809 maybe_warnx("%s is a directory", path);
1810 goto out;
1811 }
1812
1813 if (S_ISREG(sb.st_mode))
1814 handle_file(path, &sb);
1815 else
1816 maybe_warnx("%s is not a regular file", path);
1817
1818out:
1819 if (s)
1820 free(s);
1821}
1822
1823/* compress/decompress a file */
1824static void
1825handle_file(char *file, struct stat *sbp)
1826{
1827 off_t usize, gsize;
1828 char outfile[PATH_MAX];
1829
1830 infile = file;
1831 if (dflag) {
1832 usize = file_uncompress(file, outfile, sizeof(outfile));
1833#ifndef SMALL
1834 if (vflag && tflag)
1835 print_test(file, usize != -1);
1836#endif
1837 if (usize == -1)
1838 return;
1839 gsize = sbp->st_size;
1840 } else {
1841 gsize = file_compress(file, outfile, sizeof(outfile));
1842 if (gsize == -1)
1843 return;
1844 usize = sbp->st_size;
1845 }
1846
1847
1848#ifndef SMALL
1849 if (vflag && !tflag)
1850 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1851#endif
1852}
1853
1854#ifndef SMALL
1855/* this is used with -r to recursively descend directories */
1856static void
1857handle_dir(char *dir)
1858{
1859 char *path_argv[2];
1860 FTS *fts;
1861 FTSENT *entry;
1862
1863 path_argv[0] = dir;
1864 path_argv[1] = 0;
1865 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1866 if (fts == NULL) {
1867 warn("couldn't fts_open %s", dir);
1868 return;
1869 }
1870
1871 while ((entry = fts_read(fts))) {
1872 switch(entry->fts_info) {
1873 case FTS_D:
1874 case FTS_DP:
1875 continue;
1876
1877 case FTS_DNR:
1878 case FTS_ERR:
1879 case FTS_NS:
1880 maybe_warn("%s", entry->fts_path);
1881 continue;
1882 case FTS_F:
1883 handle_file(entry->fts_path, entry->fts_statp);
1884 }
1885 }
1886 (void)fts_close(fts);
1887}
1888#endif
1889
1890/* print a ratio - size reduction as a fraction of uncompressed size */
1891static void
1892print_ratio(off_t in, off_t out, FILE *where)
1893{
1894 int percent10; /* 10 * percent */
1895 off_t diff;
1896 char buff[8];
1897 int len;
1898
1899 diff = in - out/2;
1900 if (diff <= 0)
1901 /*
1902 * Output is more than double size of input! print -99.9%
1903 * Quite possibly we've failed to get the original size.
1904 */
1905 percent10 = -999;
1906 else {
1907 /*
1908 * We only need 12 bits of result from the final division,
1909 * so reduce the values until a 32bit division will suffice.
1910 */
1911 while (in > 0x100000) {
1912 diff >>= 1;
1913 in >>= 1;
1914 }
1915 if (in != 0)
1916 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1917 else
1918 percent10 = 0;
1919 }
1920
1921 len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1922 /* Move the '.' to before the last digit */
1923 buff[len - 1] = buff[len - 2];
1924 buff[len - 2] = '.';
1925 fprintf(where, "%5s%%", buff);
1926}
1927
1928#ifndef SMALL
1929/* print compression statistics, and the new name (if there is one!) */
1930static void
1931print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1932{
1933 if (file)
1934 fprintf(stderr, "%s:%s ", file,
1935 strlen(file) < 7 ? "\t\t" : "\t");
1936 print_ratio(usize, gsize, stderr);
1937 if (nfile)
1938 fprintf(stderr, " -- replaced with %s", nfile);
1939 fprintf(stderr, "\n");
1940 fflush(stderr);
1941}
1942
1943/* print test results */
1944static void
1945print_test(const char *file, int ok)
1946{
1947
1948 if (exit_value == 0 && ok == 0)
1949 exit_value = 1;
1950 fprintf(stderr, "%s:%s %s\n", file,
1951 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
1952 fflush(stderr);
1953}
1954#endif
1955
1956/* print a file's info ala --list */
1957/* eg:
1958 compressed uncompressed ratio uncompressed_name
1959 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
1960*/
1961static void
1962print_list(int fd, off_t out, const char *outfile, time_t ts)
1963{
1964 static int first = 1;
1965#ifndef SMALL
1966 static off_t in_tot, out_tot;
1967 uint32_t crc = 0;
1968#endif
1969 off_t in = 0, rv;
1970
1971 if (first) {
1972#ifndef SMALL
1973 if (vflag)
1974 printf("method crc date time ");
1975#endif
1976 if (qflag == 0)
1977 printf(" compressed uncompressed "
1978 "ratio uncompressed_name\n");
1979 }
1980 first = 0;
1981
1982 /* print totals? */
1983#ifndef SMALL
1984 if (fd == -1) {
1985 in = in_tot;
1986 out = out_tot;
1987 } else
1988#endif
1989 {
1990 /* read the last 4 bytes - this is the uncompressed size */
1991 rv = lseek(fd, (off_t)(-8), SEEK_END);
1992 if (rv != -1) {
1993 unsigned char buf[8];
1994 uint32_t usize;
1995
1996 rv = read(fd, (char *)buf, sizeof(buf));
1997 if (rv == -1)
1998 maybe_warn("read of uncompressed size");
1999 else if (rv != sizeof(buf))
2000 maybe_warnx("read of uncompressed size");
2001
2002 else {
2003 usize = buf[4] | buf[5] << 8 |
2004 buf[6] << 16 | buf[7] << 24;
2005 in = (off_t)usize;
2006#ifndef SMALL
2007 crc = buf[0] | buf[1] << 8 |
2008 buf[2] << 16 | buf[3] << 24;
2009#endif
2010 }
2011 }
2012 }
2013
2014#ifndef SMALL
2015 if (vflag && fd == -1)
2016 printf(" ");
2017 else if (vflag) {
2018 char *date = ctime(&ts);
2019
2020 /* skip the day, 1/100th second, and year */
2021 date += 4;
2022 date[12] = 0;
2023 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2024 }
2025 in_tot += in;
2026 out_tot += out;
2027#else
2028 (void)&ts; /* XXX */
2029#endif
2030 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2031 print_ratio(in, out, stdout);
2032 printf(" %s\n", outfile);
2033}
2034
2035/* display the usage of NetBSD gzip */
2036static void
2037usage(void)
2038{
2039
2040 fprintf(stderr, "%s\n", gzip_version);
2041 fprintf(stderr,
2042#ifdef SMALL
2043 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2044#else
2045 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2046 " -1 --fast fastest (worst) compression\n"
2047 " -2 .. -8 set compression level\n"
2048 " -9 --best best (slowest) compression\n"
2049 " -c --stdout write to stdout, keep original files\n"
2050 " --to-stdout\n"
2051 " -d --decompress uncompress files\n"
2052 " --uncompress\n"
2053 " -f --force force overwriting & compress links\n"
2054 " -h --help display this help\n"
2055 " -k --keep don't delete input files during operation\n"
2056 " -l --list list compressed file contents\n"
2057 " -N --name save or restore original file name and time stamp\n"
2058 " -n --no-name don't save original file name or time stamp\n"
2059 " -q --quiet output no warnings\n"
2060 " -r --recursive recursively compress files in directories\n"
2061 " -S .suf use suffix .suf instead of .gz\n"
2062 " --suffix .suf\n"
2063 " -t --test test compressed file\n"
2064 " -V --version display program version\n"
2065 " -v --verbose print extra statistics\n",
2066#endif
2067 getprogname());
2068 exit(0);
2069}
2070
2071#ifndef SMALL
2072/* display the license information of FreeBSD gzip */
2073static void
2074display_license(void)
2075{
2076
1746 }
1747
1748#ifndef SMALL
1749 if (vflag && !tflag && usize != -1 && gsize != -1)
1750 print_verbage(NULL, NULL, usize, gsize);
1751 if (vflag && tflag)
1752 print_test("(stdin)", usize != -1);
1753#endif
1754
1755}
1756
1757static void
1758handle_stdout(void)
1759{
1760 off_t gsize, usize;
1761 struct stat sb;
1762 time_t systime;
1763 uint32_t mtime;
1764 int ret;
1765
1766#ifndef SMALL
1767 if (fflag == 0 && isatty(STDOUT_FILENO)) {
1768 maybe_warnx("standard output is a terminal -- ignoring");
1769 return;
1770 }
1771#endif
1772 /* If stdin is a file use it's mtime, otherwise use current time */
1773 ret = fstat(STDIN_FILENO, &sb);
1774
1775#ifndef SMALL
1776 if (ret < 0) {
1777 maybe_warn("Can't stat stdin");
1778 return;
1779 }
1780#endif
1781
1782 if (S_ISREG(sb.st_mode))
1783 mtime = (uint32_t)sb.st_mtime;
1784 else {
1785 systime = time(NULL);
1786#ifndef SMALL
1787 if (systime == -1) {
1788 maybe_warn("time");
1789 return;
1790 }
1791#endif
1792 mtime = (uint32_t)systime;
1793 }
1794
1795 usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime);
1796#ifndef SMALL
1797 if (vflag && !tflag && usize != -1 && gsize != -1)
1798 print_verbage(NULL, NULL, usize, gsize);
1799#endif
1800}
1801
1802/* do what is asked for, for the path name */
1803static void
1804handle_pathname(char *path)
1805{
1806 char *opath = path, *s = NULL;
1807 ssize_t len;
1808 int slen;
1809 struct stat sb;
1810
1811 /* check for stdout/stdin */
1812 if (path[0] == '-' && path[1] == '\0') {
1813 if (dflag)
1814 handle_stdin();
1815 else
1816 handle_stdout();
1817 return;
1818 }
1819
1820retry:
1821 if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 &&
1822 lstat(path, &sb) != 0)) {
1823 /* lets try <path>.gz if we're decompressing */
1824 if (dflag && s == NULL && errno == ENOENT) {
1825 len = strlen(path);
1826 slen = suffixes[0].ziplen;
1827 s = malloc(len + slen + 1);
1828 if (s == NULL)
1829 maybe_err("malloc");
1830 memcpy(s, path, len);
1831 memcpy(s + len, suffixes[0].zipped, slen + 1);
1832 path = s;
1833 goto retry;
1834 }
1835 maybe_warn("can't stat: %s", opath);
1836 goto out;
1837 }
1838
1839 if (S_ISDIR(sb.st_mode)) {
1840#ifndef SMALL
1841 if (rflag)
1842 handle_dir(path);
1843 else
1844#endif
1845 maybe_warnx("%s is a directory", path);
1846 goto out;
1847 }
1848
1849 if (S_ISREG(sb.st_mode))
1850 handle_file(path, &sb);
1851 else
1852 maybe_warnx("%s is not a regular file", path);
1853
1854out:
1855 if (s)
1856 free(s);
1857}
1858
1859/* compress/decompress a file */
1860static void
1861handle_file(char *file, struct stat *sbp)
1862{
1863 off_t usize, gsize;
1864 char outfile[PATH_MAX];
1865
1866 infile = file;
1867 if (dflag) {
1868 usize = file_uncompress(file, outfile, sizeof(outfile));
1869#ifndef SMALL
1870 if (vflag && tflag)
1871 print_test(file, usize != -1);
1872#endif
1873 if (usize == -1)
1874 return;
1875 gsize = sbp->st_size;
1876 } else {
1877 gsize = file_compress(file, outfile, sizeof(outfile));
1878 if (gsize == -1)
1879 return;
1880 usize = sbp->st_size;
1881 }
1882
1883
1884#ifndef SMALL
1885 if (vflag && !tflag)
1886 print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
1887#endif
1888}
1889
1890#ifndef SMALL
1891/* this is used with -r to recursively descend directories */
1892static void
1893handle_dir(char *dir)
1894{
1895 char *path_argv[2];
1896 FTS *fts;
1897 FTSENT *entry;
1898
1899 path_argv[0] = dir;
1900 path_argv[1] = 0;
1901 fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
1902 if (fts == NULL) {
1903 warn("couldn't fts_open %s", dir);
1904 return;
1905 }
1906
1907 while ((entry = fts_read(fts))) {
1908 switch(entry->fts_info) {
1909 case FTS_D:
1910 case FTS_DP:
1911 continue;
1912
1913 case FTS_DNR:
1914 case FTS_ERR:
1915 case FTS_NS:
1916 maybe_warn("%s", entry->fts_path);
1917 continue;
1918 case FTS_F:
1919 handle_file(entry->fts_path, entry->fts_statp);
1920 }
1921 }
1922 (void)fts_close(fts);
1923}
1924#endif
1925
1926/* print a ratio - size reduction as a fraction of uncompressed size */
1927static void
1928print_ratio(off_t in, off_t out, FILE *where)
1929{
1930 int percent10; /* 10 * percent */
1931 off_t diff;
1932 char buff[8];
1933 int len;
1934
1935 diff = in - out/2;
1936 if (diff <= 0)
1937 /*
1938 * Output is more than double size of input! print -99.9%
1939 * Quite possibly we've failed to get the original size.
1940 */
1941 percent10 = -999;
1942 else {
1943 /*
1944 * We only need 12 bits of result from the final division,
1945 * so reduce the values until a 32bit division will suffice.
1946 */
1947 while (in > 0x100000) {
1948 diff >>= 1;
1949 in >>= 1;
1950 }
1951 if (in != 0)
1952 percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
1953 else
1954 percent10 = 0;
1955 }
1956
1957 len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
1958 /* Move the '.' to before the last digit */
1959 buff[len - 1] = buff[len - 2];
1960 buff[len - 2] = '.';
1961 fprintf(where, "%5s%%", buff);
1962}
1963
1964#ifndef SMALL
1965/* print compression statistics, and the new name (if there is one!) */
1966static void
1967print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
1968{
1969 if (file)
1970 fprintf(stderr, "%s:%s ", file,
1971 strlen(file) < 7 ? "\t\t" : "\t");
1972 print_ratio(usize, gsize, stderr);
1973 if (nfile)
1974 fprintf(stderr, " -- replaced with %s", nfile);
1975 fprintf(stderr, "\n");
1976 fflush(stderr);
1977}
1978
1979/* print test results */
1980static void
1981print_test(const char *file, int ok)
1982{
1983
1984 if (exit_value == 0 && ok == 0)
1985 exit_value = 1;
1986 fprintf(stderr, "%s:%s %s\n", file,
1987 strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK");
1988 fflush(stderr);
1989}
1990#endif
1991
1992/* print a file's info ala --list */
1993/* eg:
1994 compressed uncompressed ratio uncompressed_name
1995 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
1996*/
1997static void
1998print_list(int fd, off_t out, const char *outfile, time_t ts)
1999{
2000 static int first = 1;
2001#ifndef SMALL
2002 static off_t in_tot, out_tot;
2003 uint32_t crc = 0;
2004#endif
2005 off_t in = 0, rv;
2006
2007 if (first) {
2008#ifndef SMALL
2009 if (vflag)
2010 printf("method crc date time ");
2011#endif
2012 if (qflag == 0)
2013 printf(" compressed uncompressed "
2014 "ratio uncompressed_name\n");
2015 }
2016 first = 0;
2017
2018 /* print totals? */
2019#ifndef SMALL
2020 if (fd == -1) {
2021 in = in_tot;
2022 out = out_tot;
2023 } else
2024#endif
2025 {
2026 /* read the last 4 bytes - this is the uncompressed size */
2027 rv = lseek(fd, (off_t)(-8), SEEK_END);
2028 if (rv != -1) {
2029 unsigned char buf[8];
2030 uint32_t usize;
2031
2032 rv = read(fd, (char *)buf, sizeof(buf));
2033 if (rv == -1)
2034 maybe_warn("read of uncompressed size");
2035 else if (rv != sizeof(buf))
2036 maybe_warnx("read of uncompressed size");
2037
2038 else {
2039 usize = buf[4] | buf[5] << 8 |
2040 buf[6] << 16 | buf[7] << 24;
2041 in = (off_t)usize;
2042#ifndef SMALL
2043 crc = buf[0] | buf[1] << 8 |
2044 buf[2] << 16 | buf[3] << 24;
2045#endif
2046 }
2047 }
2048 }
2049
2050#ifndef SMALL
2051 if (vflag && fd == -1)
2052 printf(" ");
2053 else if (vflag) {
2054 char *date = ctime(&ts);
2055
2056 /* skip the day, 1/100th second, and year */
2057 date += 4;
2058 date[12] = 0;
2059 printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
2060 }
2061 in_tot += in;
2062 out_tot += out;
2063#else
2064 (void)&ts; /* XXX */
2065#endif
2066 printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
2067 print_ratio(in, out, stdout);
2068 printf(" %s\n", outfile);
2069}
2070
2071/* display the usage of NetBSD gzip */
2072static void
2073usage(void)
2074{
2075
2076 fprintf(stderr, "%s\n", gzip_version);
2077 fprintf(stderr,
2078#ifdef SMALL
2079 "usage: %s [-" OPT_LIST "] [<file> [<file> ...]]\n",
2080#else
2081 "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [<file> [<file> ...]]\n"
2082 " -1 --fast fastest (worst) compression\n"
2083 " -2 .. -8 set compression level\n"
2084 " -9 --best best (slowest) compression\n"
2085 " -c --stdout write to stdout, keep original files\n"
2086 " --to-stdout\n"
2087 " -d --decompress uncompress files\n"
2088 " --uncompress\n"
2089 " -f --force force overwriting & compress links\n"
2090 " -h --help display this help\n"
2091 " -k --keep don't delete input files during operation\n"
2092 " -l --list list compressed file contents\n"
2093 " -N --name save or restore original file name and time stamp\n"
2094 " -n --no-name don't save original file name or time stamp\n"
2095 " -q --quiet output no warnings\n"
2096 " -r --recursive recursively compress files in directories\n"
2097 " -S .suf use suffix .suf instead of .gz\n"
2098 " --suffix .suf\n"
2099 " -t --test test compressed file\n"
2100 " -V --version display program version\n"
2101 " -v --verbose print extra statistics\n",
2102#endif
2103 getprogname());
2104 exit(0);
2105}
2106
2107#ifndef SMALL
2108/* display the license information of FreeBSD gzip */
2109static void
2110display_license(void)
2111{
2112
2077 fprintf(stderr, "%s (based on NetBSD gzip 20091011)\n", gzip_version);
2113 fprintf(stderr, "%s (based on NetBSD gzip 20111009)\n", gzip_version);
2078 fprintf(stderr, "%s\n", gzip_copyright);
2079 exit(0);
2080}
2081#endif
2082
2083/* display the version of NetBSD gzip */
2084static void
2085display_version(void)
2086{
2087
2088 fprintf(stderr, "%s\n", gzip_version);
2089 exit(0);
2090}
2091
2092#ifndef NO_BZIP2_SUPPORT
2093#include "unbzip2.c"
2094#endif
2095#ifndef NO_COMPRESS_SUPPORT
2096#include "zuncompress.c"
2097#endif
2098#ifndef NO_PACK_SUPPORT
2099#include "unpack.c"
2100#endif
2114 fprintf(stderr, "%s\n", gzip_copyright);
2115 exit(0);
2116}
2117#endif
2118
2119/* display the version of NetBSD gzip */
2120static void
2121display_version(void)
2122{
2123
2124 fprintf(stderr, "%s\n", gzip_version);
2125 exit(0);
2126}
2127
2128#ifndef NO_BZIP2_SUPPORT
2129#include "unbzip2.c"
2130#endif
2131#ifndef NO_COMPRESS_SUPPORT
2132#include "zuncompress.c"
2133#endif
2134#ifndef NO_PACK_SUPPORT
2135#include "unpack.c"
2136#endif
2137#ifndef NO_XZ_SUPPORT
2138#include "unxz.c"
2139#endif
2101
2102static ssize_t
2103read_retry(int fd, void *buf, size_t sz)
2104{
2105 char *cp = buf;
2106 size_t left = MIN(sz, (size_t) SSIZE_MAX);
2107
2108 while (left > 0) {
2109 ssize_t ret;
2110
2111 ret = read(fd, cp, left);
2112 if (ret == -1) {
2113 return ret;
2114 } else if (ret == 0) {
2115 break; /* EOF */
2116 }
2117 cp += ret;
2118 left -= ret;
2119 }
2120
2121 return sz - left;
2122}
2140
2141static ssize_t
2142read_retry(int fd, void *buf, size_t sz)
2143{
2144 char *cp = buf;
2145 size_t left = MIN(sz, (size_t) SSIZE_MAX);
2146
2147 while (left > 0) {
2148 ssize_t ret;
2149
2150 ret = read(fd, cp, left);
2151 if (ret == -1) {
2152 return ret;
2153 } else if (ret == 0) {
2154 break; /* EOF */
2155 }
2156 cp += ret;
2157 left -= ret;
2158 }
2159
2160 return sz - left;
2161}