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