~ubuntu-branches/ubuntu/oneiric/pristine-tar/oneiric

« back to all changes in this revision

Viewing changes to zgz.c

  • Committer: Bazaar Package Importer
  • Author(s): Joey Hess
  • Date: 2009-01-22 15:07:33 UTC
  • mfrom: (4.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090122150733-ph8jlww8a28sbt4t
Tags: 0.21
Add support for GIT_DIR. Closes: #512619

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Author: Faidon Liambotis <paravoid@debian.org>
3
 
 *
4
 
 * This is a zlib-based gzip that is heavily based on NetBSD's gzip,
5
 
 * developed by Matthew R. Green.
6
 
 *
7
 
 * This is suited for gzip regeneration and is part of pristine-tar.
8
 
 * As such, it adds some extra options which are needed to successfully
9
 
 * reproduce the gzips out there and removes features of the original
10
 
 * implementation that were not relevant (e.g. decompression)
11
 
 *
12
 
 * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green
13
 
 * Copyright (c) 2007 Faidon Liambotis
14
 
 * All rights reserved.
15
 
 *
16
 
 * Redistribution and use in source and binary forms, with or without
17
 
 * modification, are permitted provided that the following conditions
18
 
 * are met:
19
 
 * 1. Redistributions of source code must retain the above copyright
20
 
 *    notice, this list of conditions and the following disclaimer.
21
 
 * 2. Redistributions in binary form must reproduce the above copyright
22
 
 *    notice, this list of conditions and the following disclaimer in the
23
 
 *    documentation and/or other materials provided with the distribution.
24
 
 * 3. The name of the author may not be used to endorse or promote products
25
 
 *    derived from this software without specific prior written permission.
26
 
 *
27
 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
28
 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29
 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
30
 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
31
 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32
 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
34
 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
35
 
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36
 
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37
 
 * SUCH DAMAGE.
38
 
 */
39
 
 
40
 
/*
41
 
 * gzip.c -- GPL free gzip using zlib.
42
 
 *
43
 
 * RFC 1950 covers the zlib format
44
 
 * RFC 1951 covers the deflate format
45
 
 * RFC 1952 covers the gzip format
46
 
 *
47
 
 */
48
 
 
49
 
#define _GNU_SOURCE
50
 
 
51
 
#include <sys/param.h>
52
 
#include <sys/stat.h>
53
 
#include <sys/time.h>
54
 
 
55
 
#include <inttypes.h>
56
 
#include <unistd.h>
57
 
#include <stdio.h>
58
 
#include <string.h>
59
 
#include <stdlib.h>
60
 
#include <err.h>
61
 
#include <errno.h>
62
 
#include <fcntl.h>
63
 
#include <zlib.h>
64
 
#include <fts.h>
65
 
#include <libgen.h>
66
 
#include <stdarg.h>
67
 
#include <getopt.h>
68
 
#include <time.h>
69
 
 
70
 
#ifndef PRIdOFF
71
 
#define PRIdOFF PRId64
72
 
#endif
73
 
 
74
 
/* what type of file are we dealing with */
75
 
enum filetype {
76
 
        FT_GZIP,
77
 
        FT_LAST,
78
 
        FT_UNKNOWN
79
 
};
80
 
 
81
 
#define GZ_SUFFIX       ".gz"
82
 
 
83
 
#define BUFLEN          (64 * 1024)
84
 
 
85
 
#define GZIP_MAGIC0     0x1F
86
 
#define GZIP_MAGIC1     0x8B
87
 
#define GZIP_OMAGIC1    0x9E
88
 
 
89
 
#define GZIP_TIMESTAMP  (off_t)4
90
 
#define GZIP_ORIGNAME   (off_t)10
91
 
 
92
 
#define HEAD_CRC        0x02
93
 
#define EXTRA_FIELD     0x04
94
 
#define ORIG_NAME       0x08
95
 
#define COMMENT         0x10
96
 
 
97
 
#define GZIP_OS_UNIX    3       /* Unix */
98
 
#define GZIP_OS_NTFS    11      /* NTFS */
99
 
 
100
 
typedef struct {
101
 
    const char  *zipped;
102
 
    int         ziplen;
103
 
    const char  *normal;        /* for unzip - must not be longer than zipped */
104
 
} suffixes_t;
105
 
static suffixes_t suffixes[] = {
106
 
#define SUFFIX(Z, N) {Z, sizeof Z - 1, N}
107
 
        SUFFIX(GZ_SUFFIX,       ""),    /* Overwritten by -S .xxx */
108
 
        SUFFIX(GZ_SUFFIX,       ""),
109
 
        SUFFIX(".z",            ""),
110
 
        SUFFIX("-gz",           ""),
111
 
        SUFFIX("-z",            ""),
112
 
        SUFFIX("_z",            ""),
113
 
        SUFFIX(".taz",          ".tar"),
114
 
        SUFFIX(".tgz",          ".tar"),
115
 
        SUFFIX(GZ_SUFFIX,       ""),    /* Overwritten by -S "" */
116
 
#undef SUFFIX
117
 
};
118
 
#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0])
119
 
 
120
 
static  const char      gzip_version[] = "zgz 20071002 based on NetBSD gzip 20060927";
121
 
 
122
 
static  const char      gzip_copyright[] = \
123
 
" Author: Faidon Liambotis <paravoid@debian.org>\n"
124
 
"\n"
125
 
" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n"
126
 
" Copyright (c) 2007 Faidon Liambotis\n"
127
 
" * All rights reserved.\n"
128
 
" *\n"
129
 
" * Redistribution and use in source and binary forms, with or without\n"
130
 
" * modification, are permitted provided that the following conditions\n"
131
 
" * are met:\n"
132
 
" * 1. Redistributions of source code must retain the above copyright\n"
133
 
" *    notice, this list of conditions and the following disclaimer.\n"
134
 
" * 2. Redistributions in binary form must reproduce the above copyright\n"
135
 
" *    notice, this list of conditions and the following disclaimer in the\n"
136
 
" *    documentation and/or other materials provided with the distribution.\n"
137
 
" * 3. The name of the author may not be used to endorse or promote products\n"
138
 
" *    derived from this software without specific prior written permission.\n"
139
 
" *\n"
140
 
" * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
141
 
" * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"
142
 
" * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"
143
 
" * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"
144
 
" * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n"
145
 
" * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n"
146
 
" * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n"
147
 
" * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n"
148
 
" * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n"
149
 
" * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n"
150
 
" * SUCH DAMAGE.";
151
 
 
152
 
static  int     cflag;                  /* stdout mode */
153
 
static  int     lflag;                  /* list mode */
154
 
static  int     numflag = 6;            /* gzip -1..-9 value */
155
 
 
156
 
static  int     fflag;                  /* force mode */
157
 
static  int     nflag;                  /* don't save name (impiles -m) */
158
 
static  int     Nflag;                  /* restore name/timestamp */
159
 
static  int     mflag;                  /* undocumented: don't save timestamp */
160
 
static  int     qflag;                  /* quiet mode */
161
 
static  int     tflag;                  /* test */
162
 
static  int     vflag;                  /* verbose mode */
163
 
static  int     xflag = -1;             /* don't set Extra Flags (i.e. compression level)
164
 
                                           binary compatibility with an older, buggy version */
165
 
static  int     osflag = GZIP_OS_UNIX;  /* Unix or otherwise */
166
 
static  int     ntfs_quirk = 0;         /* whether NTFS quirk is activated */
167
 
static  int     exit_value = 0;         /* exit value */
168
 
 
169
 
static  char    *infile;                /* name of file coming in */
170
 
 
171
 
static  void    maybe_err(const char *fmt, ...)
172
 
    __attribute__((__format__(__printf__, 1, 2)));
173
 
static  void    maybe_warn(const char *fmt, ...)
174
 
    __attribute__((__format__(__printf__, 1, 2)));
175
 
static  void    maybe_warnx(const char *fmt, ...)
176
 
    __attribute__((__format__(__printf__, 1, 2)));
177
 
#if XXX_INPUT
178
 
static  enum filetype file_gettype(u_char *);
179
 
#endif
180
 
static  off_t   gz_compress(int, int, off_t *, const char *, uint32_t);
181
 
static  off_t   file_compress(char *, char *, char *, size_t);
182
 
static  void    handle_pathname(char *, char *);
183
 
static  void    handle_file(char *, char *, struct stat *);
184
 
static  void    handle_stdout(char *);
185
 
static  void    print_ratio(off_t, off_t, FILE *);
186
 
static  void    print_list(int fd, off_t, const char *, time_t);
187
 
static  void    usage(void);
188
 
static  void    display_version(void);
189
 
static  void    display_license(void);
190
 
static  const suffixes_t *check_suffix(char *, int);
191
 
 
192
 
static  void    print_verbage(const char *, const char *, off_t, off_t);
193
 
static  void    copymodes(int fd, const struct stat *, const char *file);
194
 
static  int     check_outfile(const char *outfile);
195
 
 
196
 
int main(int, char **p);
197
 
 
198
 
static const struct option longopts[] = {
199
 
        { "stdout",             no_argument,            0,      'c' },
200
 
        { "to-stdout",          no_argument,            0,      'c' },
201
 
        { "decompress",         no_argument,            0,      'd' },
202
 
        { "uncompress",         no_argument,            0,      'd' },
203
 
        { "force",              no_argument,            0,      'f' },
204
 
        { "help",               no_argument,            0,      'h' },
205
 
        { "list",               no_argument,            0,      'l' },
206
 
        { "no-name",            no_argument,            0,      'n' },
207
 
        { "name",               no_argument,            0,      'N' },
208
 
        { "quiet",              no_argument,            0,      'q' },
209
 
        { "recursive",          no_argument,            0,      'r' },
210
 
        { "suffix",             required_argument,      0,      'S' },
211
 
        { "test",               no_argument,            0,      't' },
212
 
        { "verbose",            no_argument,            0,      'v' },
213
 
        { "fast",               no_argument,            0,      '1' },
214
 
        { "best",               no_argument,            0,      '9' },
215
 
        { "ascii",              no_argument,            0,      'a' },
216
 
        /* new options */
217
 
        { "no-timestamp",       no_argument,            0,      'm' },
218
 
        { "force-timestamp",    no_argument,            0,      'M' },
219
 
        { "osflag",             required_argument,      0,      's' },
220
 
        { "original-name",      required_argument,      0,      'o' },
221
 
        { "quirk",              required_argument,      0,      'k' },
222
 
        /* end */
223
 
        { "version",            no_argument,            0,      'V' },
224
 
        { "license",            no_argument,            0,      'L' },
225
 
        { NULL,                 no_argument,            0,       0  },
226
 
};
227
 
 
228
 
int
229
 
main(int argc, char **argv)
230
 
{
231
 
        const char *progname = argv[0];
232
 
        char origname[BUFLEN] = { 0 };
233
 
        char *gzip;
234
 
        int len;
235
 
        int ch;
236
 
 
237
 
        if (strcmp(progname, "gunzip") == 0 ||
238
 
            strcmp(progname, "zcat") == 0 ||
239
 
            strcmp(progname, "gzcat") == 0) {
240
 
                fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
241
 
                usage();
242
 
        }
243
 
 
244
 
        if (argc > 1 && strcmp(argv[1], "--gnu") == 0) {
245
 
                /* omit first argument, i.e. --gnu */
246
 
                argv++;
247
 
                /* works because "--gnu" is bigger than "gzip" */
248
 
                strcpy(argv[0], "gzip"); 
249
 
                execv("/bin/gzip", argv);
250
 
 
251
 
                /* NOT REACHED */
252
 
                fprintf(stderr, "Failed to spawn /bin/gzip\n");
253
 
                exit(1);
254
 
        } else if (argc > 1 && strcmp(argv[1], "--zlib") == 0) {
255
 
                /* skip --zlib argument if existent */
256
 
                argc--;
257
 
                argv++;
258
 
        }
259
 
 
260
 
#define OPT_LIST "123456789acdfhlLNnMmqrS:tVvo:k:s:"
261
 
 
262
 
        while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) {
263
 
                switch (ch) {
264
 
                case '1': case '2': case '3':
265
 
                case '4': case '5': case '6':
266
 
                case '7': case '8': case '9':
267
 
                        numflag = ch - '0';
268
 
                        break;
269
 
                case 'c':
270
 
                        cflag = 1;
271
 
                        break;
272
 
                case 'l':
273
 
                        lflag = 1;
274
 
                        break;
275
 
                case 'f':
276
 
                        fflag = 1;
277
 
                        break;
278
 
                case 'N':
279
 
                        nflag = 0;
280
 
                        Nflag = 1;
281
 
                        break;
282
 
                case 'n':
283
 
                        nflag = 1;
284
 
                        Nflag = 0;
285
 
                        /* no break, n implies m */
286
 
                case 'm':
287
 
                        mflag = 1;
288
 
                        break;
289
 
                case 'M':
290
 
                        mflag = 0;
291
 
                        break;
292
 
                case 'q':
293
 
                        qflag = 1;
294
 
                        break;
295
 
                case 'S':
296
 
                        len = strlen(optarg);
297
 
                        if (len != 0) {
298
 
                                suffixes[0].zipped = optarg;
299
 
                                suffixes[0].ziplen = len;
300
 
                        } else {
301
 
                                suffixes[NUM_SUFFIXES - 1].zipped = "";
302
 
                                suffixes[NUM_SUFFIXES - 1].ziplen = 0;
303
 
                        }
304
 
                        break;
305
 
                case 't':
306
 
                        cflag = 1;
307
 
                        tflag = 1;
308
 
                        break;
309
 
                case 'v':
310
 
                        vflag = 1;
311
 
                        break;
312
 
                case 's':
313
 
                        osflag = atoi(optarg);
314
 
                        break;
315
 
                case 'o':
316
 
                        if (nflag)
317
 
                                fprintf(stderr, "%s: ignoring original-name because no-name was passed\n", progname);
318
 
                        strncpy(origname, optarg, BUFLEN);
319
 
                        break;
320
 
                case 'k':
321
 
                        if (strcmp(optarg, "buggy-bsd") == 0) {
322
 
                                /* certain archives made with older versions of
323
 
                                 * BSD variants of gzip */
324
 
 
325
 
                                /* no name or timestamp information */
326
 
                                nflag = 1;
327
 
                                mflag = 1;
328
 
                                /* maximum compression but without indicating so */
329
 
                                numflag = 9;
330
 
                                xflag = 0;
331
 
                        } else if (strcmp(optarg, "ntfs") == 0) {
332
 
                                ntfs_quirk = 1;
333
 
                                /* no name or timestamp information */
334
 
                                nflag = 1;
335
 
                                mflag = 1;
336
 
                                /* osflag is NTFS */
337
 
                                osflag = GZIP_OS_NTFS;
338
 
                        } else {
339
 
                                fprintf(stderr, "%s: unknown quirk!\n", progname);
340
 
                                usage();
341
 
                        }
342
 
                        break;
343
 
                case 'r':
344
 
                        fprintf(stderr, "%s: recursive is not supported on this version\n", progname);
345
 
                        usage();
346
 
                        break;
347
 
                case 'd':
348
 
                        fprintf(stderr, "%s: decompression is not supported on this version\n", progname);
349
 
                        usage();
350
 
                        break;
351
 
                case 'a':
352
 
                        fprintf(stderr, "%s: option --ascii ignored on this version\n", progname);
353
 
                        break;
354
 
                case 'V':
355
 
                        display_version();
356
 
                        /* NOTREACHED */
357
 
                case 'L':
358
 
                        display_license();
359
 
                        /* NOT REACHED */
360
 
                default:
361
 
                        usage();
362
 
                        /* NOTREACHED */
363
 
                }
364
 
        }
365
 
        argv += optind;
366
 
        argc -= optind;
367
 
 
368
 
        if (argc == 0) {
369
 
                handle_stdout(origname);
370
 
        } else {
371
 
                do {
372
 
                        handle_pathname(argv[0], origname);
373
 
                } while (*++argv);
374
 
        }
375
 
        if (qflag == 0 && lflag && argc > 1)
376
 
                print_list(-1, 0, "(totals)", 0);
377
 
        exit(exit_value);
378
 
}
379
 
 
380
 
/* maybe print a warning */
381
 
void
382
 
maybe_warn(const char *fmt, ...)
383
 
{
384
 
        va_list ap;
385
 
 
386
 
        if (qflag == 0) {
387
 
                va_start(ap, fmt);
388
 
                vwarn(fmt, ap);
389
 
                va_end(ap);
390
 
        }
391
 
        if (exit_value == 0)
392
 
                exit_value = 1;
393
 
}
394
 
 
395
 
/* ... without an errno. */
396
 
void
397
 
maybe_warnx(const char *fmt, ...)
398
 
{
399
 
        va_list ap;
400
 
 
401
 
        if (qflag == 0) {
402
 
                va_start(ap, fmt);
403
 
                vwarnx(fmt, ap);
404
 
                va_end(ap);
405
 
        }
406
 
        if (exit_value == 0)
407
 
                exit_value = 1;
408
 
}
409
 
 
410
 
/* maybe print an error */
411
 
void
412
 
maybe_err(const char *fmt, ...)
413
 
{
414
 
        va_list ap;
415
 
 
416
 
        if (qflag == 0) {
417
 
                va_start(ap, fmt);
418
 
                vwarn(fmt, ap);
419
 
                va_end(ap);
420
 
        }
421
 
        exit(2);
422
 
}
423
 
 
424
 
/* compress input to output. Return bytes read, -1 on error */
425
 
static off_t
426
 
gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime)
427
 
{
428
 
        z_stream z;
429
 
        char *outbufp, *inbufp;
430
 
        off_t in_tot = 0, out_tot = 0;
431
 
        ssize_t in_size;
432
 
        int i, error;
433
 
        uLong crc;
434
 
 
435
 
        outbufp = malloc(BUFLEN);
436
 
        inbufp = malloc(BUFLEN);
437
 
        if (outbufp == NULL || inbufp == NULL) {
438
 
                maybe_err("malloc failed");
439
 
                goto out;
440
 
        }
441
 
 
442
 
        memset(&z, 0, sizeof z);
443
 
        z.zalloc = Z_NULL;
444
 
        z.zfree = Z_NULL;
445
 
        z.opaque = 0;
446
 
 
447
 
        if (mflag != 0)
448
 
                mtime = 0;
449
 
        if (nflag != 0)
450
 
                origname = "";
451
 
 
452
 
        i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", 
453
 
                     GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED,
454
 
                     *origname ? ORIG_NAME : 0,
455
 
                     mtime & 0xff,
456
 
                     (mtime >> 8) & 0xff,
457
 
                     (mtime >> 16) & 0xff,
458
 
                     (mtime >> 24) & 0xff,
459
 
                     xflag >= 0 ? xflag :
460
 
                     numflag == 1 ? 4 : numflag == 9 ? 2 : 0,
461
 
                     osflag, origname);
462
 
        if (i >= BUFLEN)     
463
 
                /* this need PATH_MAX > BUFLEN ... */
464
 
                maybe_err("snprintf");
465
 
        if (*origname)
466
 
                i++;
467
 
 
468
 
        z.next_out = (unsigned char *)outbufp + i;
469
 
        z.avail_out = BUFLEN - i;
470
 
 
471
 
        error = deflateInit2(&z, numflag, Z_DEFLATED,
472
 
                             (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY);
473
 
        if (error != Z_OK) {
474
 
                maybe_warnx("deflateInit2 failed");
475
 
                in_tot = -1;
476
 
                goto out;
477
 
        }
478
 
 
479
 
        crc = crc32(0L, Z_NULL, 0);
480
 
        for (;;) {
481
 
                if (z.avail_out == 0) {
482
 
                        if (write(out, outbufp, BUFLEN) != BUFLEN) {
483
 
                                maybe_warn("write");
484
 
                                in_tot = -1;
485
 
                                goto out;
486
 
                        }
487
 
 
488
 
                        out_tot += BUFLEN;
489
 
                        z.next_out = (unsigned char *)outbufp;
490
 
                        z.avail_out = BUFLEN;
491
 
                }
492
 
 
493
 
                if (z.avail_in == 0) {
494
 
                        in_size = read(in, inbufp, BUFLEN);
495
 
                        if (in_size < 0) {
496
 
                                maybe_warn("read");
497
 
                                in_tot = -1;
498
 
                                goto out;
499
 
                        }
500
 
                        if (in_size == 0)
501
 
                                break;
502
 
 
503
 
                        crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size);
504
 
                        in_tot += in_size;
505
 
                        z.next_in = (unsigned char *)inbufp;
506
 
                        z.avail_in = in_size;
507
 
                }
508
 
 
509
 
                error = deflate(&z, Z_NO_FLUSH);
510
 
                if (error != Z_OK && error != Z_STREAM_END) {
511
 
                        maybe_warnx("deflate failed");
512
 
                        in_tot = -1;
513
 
                        goto out;
514
 
                }
515
 
        }
516
 
 
517
 
        /* clean up */
518
 
        for (;;) {
519
 
                size_t len;
520
 
                ssize_t w;
521
 
 
522
 
                error = deflate(&z, Z_FINISH);
523
 
                if (error != Z_OK && error != Z_STREAM_END) {
524
 
                        maybe_warnx("deflate failed");
525
 
                        in_tot = -1;
526
 
                        goto out;
527
 
                }
528
 
 
529
 
                len = (char *)z.next_out - outbufp;
530
 
 
531
 
                /* for a really strange reason, that 
532
 
                 * particular byte is decremented */
533
 
                if (ntfs_quirk)
534
 
                        outbufp[10]--;
535
 
 
536
 
                w = write(out, outbufp, len);
537
 
                if (w == -1 || (size_t)w != len) {
538
 
                        maybe_warn("write");
539
 
                        out_tot = -1;
540
 
                        goto out;
541
 
                }
542
 
                out_tot += len;
543
 
                z.next_out = (unsigned char *)outbufp;
544
 
                z.avail_out = BUFLEN;
545
 
 
546
 
                if (error == Z_STREAM_END)
547
 
                        break;
548
 
        }
549
 
 
550
 
        if (deflateEnd(&z) != Z_OK) {
551
 
                maybe_warnx("deflateEnd failed");
552
 
                in_tot = -1;
553
 
                goto out;
554
 
        }
555
 
 
556
 
        if (ntfs_quirk) {
557
 
                /* write NTFS tail magic (?) */
558
 
                i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c", 
559
 
                        0x00, 0x00, 0xff, 0xff, 0x03, 0x00);
560
 
                if (i != 6)
561
 
                        maybe_err("snprintf");
562
 
                if (write(out, outbufp, i) != i) {
563
 
                        maybe_warn("write");
564
 
                        in_tot = -1;
565
 
                } else
566
 
                        out_tot += i;
567
 
        }
568
 
 
569
 
        /* write CRC32 and input size (ISIZE) at the tail */
570
 
        i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", 
571
 
                 (int)crc & 0xff,
572
 
                 (int)(crc >> 8) & 0xff,
573
 
                 (int)(crc >> 16) & 0xff,
574
 
                 (int)(crc >> 24) & 0xff,
575
 
                 (int)in_tot & 0xff,
576
 
                 (int)(in_tot >> 8) & 0xff,
577
 
                 (int)(in_tot >> 16) & 0xff,
578
 
                 (int)(in_tot >> 24) & 0xff);
579
 
        if (i != 8)
580
 
                maybe_err("snprintf");
581
 
        if (write(out, outbufp, i) != i) {
582
 
                maybe_warn("write");
583
 
                in_tot = -1;
584
 
        } else
585
 
                out_tot += i;
586
 
 
587
 
out:
588
 
        if (inbufp != NULL)
589
 
                free(inbufp);
590
 
        if (outbufp != NULL)
591
 
                free(outbufp);
592
 
        if (gsizep)
593
 
                *gsizep = out_tot;
594
 
        return in_tot;
595
 
}
596
 
 
597
 
 
598
 
/*
599
 
 * set the owner, mode, flags & utimes using the given file descriptor.
600
 
 * file is only used in possible warning messages.
601
 
 */
602
 
static void
603
 
copymodes(int fd, const struct stat *sbp, const char *file)
604
 
{
605
 
        struct stat sb;
606
 
 
607
 
        /*
608
 
         * If we have no info on the input, give this file some
609
 
         * default values and return..
610
 
         */
611
 
        if (sbp == NULL) {
612
 
                mode_t mask = umask(022);
613
 
 
614
 
                (void)fchmod(fd, DEFFILEMODE & ~mask);
615
 
                (void)umask(mask);
616
 
                return; 
617
 
        }
618
 
        sb = *sbp;
619
 
 
620
 
        /* if the chown fails, remove set-id bits as-per compress(1) */
621
 
        if (fchown(fd, sb.st_uid, sb.st_gid) < 0) {
622
 
                if (errno != EPERM)
623
 
                        maybe_warn("couldn't fchown: %s", file);
624
 
                sb.st_mode &= ~(S_ISUID|S_ISGID);
625
 
        }
626
 
 
627
 
        /* we only allow set-id and the 9 normal permission bits */
628
 
        sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO;
629
 
        if (fchmod(fd, sb.st_mode) < 0)
630
 
                maybe_warn("couldn't fchmod: %s", file);
631
 
}
632
 
 
633
 
#if INPUT
634
 
/* what sort of file is this? */
635
 
static enum filetype
636
 
file_gettype(u_char *buf)
637
 
{
638
 
 
639
 
        if (buf[0] == GZIP_MAGIC0 &&
640
 
            (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1))
641
 
                return FT_GZIP;
642
 
        else
643
 
                return FT_UNKNOWN;
644
 
}
645
 
#endif
646
 
 
647
 
/* check the outfile is OK. */
648
 
static int
649
 
check_outfile(const char *outfile)
650
 
{
651
 
        struct stat sb;
652
 
        int ok = 1;
653
 
 
654
 
        if (lflag == 0 && stat(outfile, &sb) == 0) {
655
 
                if (fflag)
656
 
                        unlink(outfile);
657
 
                else if (isatty(STDIN_FILENO)) {
658
 
                        char ans[10] = { 'n', '\0' };   /* default */
659
 
 
660
 
                        fprintf(stderr, "%s already exists -- do you wish to "
661
 
                                        "overwrite (y or n)? " , outfile);
662
 
                        (void)fgets(ans, sizeof(ans) - 1, stdin);
663
 
                        if (ans[0] != 'y' && ans[0] != 'Y') {
664
 
                                fprintf(stderr, "\tnot overwritting\n");
665
 
                                ok = 0;
666
 
                        } else
667
 
                                unlink(outfile);
668
 
                } else {
669
 
                        maybe_warnx("%s already exists -- skipping", outfile);
670
 
                        ok = 0;
671
 
                }
672
 
        }
673
 
        return ok;
674
 
}
675
 
 
676
 
static void
677
 
unlink_input(const char *file, const struct stat *sb)
678
 
{
679
 
        struct stat nsb;
680
 
 
681
 
        if (stat(file, &nsb) != 0)
682
 
                /* Must be gone alrady */
683
 
                return;
684
 
        if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino)
685
 
                /* Definitely a different file */
686
 
                return;
687
 
        unlink(file);
688
 
}
689
 
 
690
 
static const suffixes_t *
691
 
check_suffix(char *file, int xlate)
692
 
{
693
 
        const suffixes_t *s;
694
 
        int len = strlen(file);
695
 
        char *sp;
696
 
 
697
 
        for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) {
698
 
                /* if it doesn't fit in "a.suf", don't bother */
699
 
                if (s->ziplen >= len)
700
 
                        continue;
701
 
                sp = file + len - s->ziplen;
702
 
                if (strcmp(s->zipped, sp) != 0)
703
 
                        continue;
704
 
                if (xlate)
705
 
                        strcpy(sp, s->normal);
706
 
                return s;
707
 
        }
708
 
        return NULL;
709
 
}
710
 
 
711
 
/*
712
 
 * compress the given file: create a corresponding .gz file and remove the
713
 
 * original.
714
 
 */
715
 
static off_t
716
 
file_compress(char *file, char *origname, char *outfile, size_t outsize)
717
 
{
718
 
        int in;
719
 
        int out;
720
 
        off_t size, insize;
721
 
        struct stat isb, osb;
722
 
        const suffixes_t *suff;
723
 
 
724
 
        in = open(file, O_RDONLY);
725
 
        if (in == -1) {
726
 
                maybe_warn("can't open %s", file);
727
 
                return -1;
728
 
        }
729
 
 
730
 
        if (cflag == 0) {
731
 
                if (fstat(in, &isb) == 0) {
732
 
                        if (isb.st_nlink > 1 && fflag == 0) {
733
 
                                maybe_warnx("%s has %d other link%s -- "
734
 
                                            "skipping", file, isb.st_nlink - 1,
735
 
                                            isb.st_nlink == 1 ? "" : "s");
736
 
                                close(in);
737
 
                                return -1;
738
 
                        }
739
 
                }
740
 
 
741
 
                if (fflag == 0 && (suff = check_suffix(file, 0))
742
 
                    && suff->zipped[0] != 0) {
743
 
                        maybe_warnx("%s already has %s suffix -- unchanged",
744
 
                                    file, suff->zipped);
745
 
                        close(in);
746
 
                        return -1;
747
 
                }
748
 
 
749
 
                /* Add (usually) .gz to filename */
750
 
                if ((size_t)snprintf(outfile, outsize, "%s%s",
751
 
                                        file, suffixes[0].zipped) >= outsize)
752
 
                        memcpy(outfile - suffixes[0].ziplen - 1,
753
 
                                suffixes[0].zipped, suffixes[0].ziplen + 1);
754
 
 
755
 
                if (check_outfile(outfile) == 0) {
756
 
                        close(in);
757
 
                        return -1;
758
 
                }
759
 
 
760
 
                out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600);
761
 
                if (out == -1) {
762
 
                        maybe_warn("could not create output: %s", outfile);
763
 
                        fclose(stdin);
764
 
                        return -1;
765
 
                }
766
 
        } else {
767
 
                out = STDOUT_FILENO;
768
 
        }
769
 
 
770
 
        insize = gz_compress(in, out, &size, origname, (uint32_t)isb.st_mtime);
771
 
 
772
 
        (void)close(in);
773
 
 
774
 
        /*
775
 
         * If there was an error, insize will be -1.
776
 
         * If we compressed to stdout, just return the size.
777
 
         * Otherwise stat the file and check it is the correct size.
778
 
         * We only blow away the file if we can stat the output and it
779
 
         * has the expected size.
780
 
         */
781
 
        if (cflag != 0)
782
 
                return insize == -1 ? -1 : size;
783
 
 
784
 
        if (fstat(out, &osb) != 0) {
785
 
                maybe_warn("couldn't stat: %s", outfile);
786
 
                goto bad_outfile;
787
 
        }
788
 
 
789
 
        if (osb.st_size != size) {
790
 
                maybe_warnx("output file: %s wrong size (%" PRIdOFF
791
 
                                " != %" PRIdOFF "), deleting",
792
 
                                outfile, (long long)osb.st_size,
793
 
                                (long long)size);
794
 
                goto bad_outfile;
795
 
        }
796
 
 
797
 
        copymodes(out, &isb, outfile);
798
 
        if (close(out) == -1)
799
 
                maybe_warn("couldn't close output");
800
 
 
801
 
        /* output is good, ok to delete input */
802
 
        unlink_input(file, &isb);
803
 
        return size;
804
 
 
805
 
    bad_outfile:
806
 
        if (close(out) == -1)
807
 
                maybe_warn("couldn't close output");
808
 
 
809
 
        maybe_warnx("leaving original %s", file);
810
 
        unlink(outfile);
811
 
        return size;
812
 
}
813
 
 
814
 
static void
815
 
handle_stdout(char *origname)
816
 
{
817
 
        off_t gsize, usize;
818
 
        struct stat sb;
819
 
        time_t systime;
820
 
        uint32_t mtime;
821
 
        int ret;
822
 
 
823
 
        if (fflag == 0 && isatty(STDOUT_FILENO)) {
824
 
                maybe_warnx("standard output is a terminal -- ignoring");
825
 
                return;
826
 
        }
827
 
        /* If stdin is a file use it's mtime, otherwise use current time */
828
 
        ret = fstat(STDIN_FILENO, &sb);
829
 
 
830
 
        if (ret < 0) {
831
 
                maybe_warn("Can't stat stdin");
832
 
                return;
833
 
        }
834
 
 
835
 
        if (S_ISREG(sb.st_mode))
836
 
                mtime = (uint32_t)sb.st_mtime;
837
 
        else {
838
 
                systime = time(NULL);
839
 
                if (systime == -1) {
840
 
                        maybe_warn("time");
841
 
                        return;
842
 
                } 
843
 
                mtime = (uint32_t)systime;
844
 
        }
845
 
                        
846
 
        usize = gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, origname, mtime);
847
 
        if (vflag && !tflag && usize != -1 && gsize != -1)
848
 
                print_verbage(NULL, NULL, usize, gsize);
849
 
}
850
 
 
851
 
/* do what is asked for, for the path name */
852
 
static void
853
 
handle_pathname(char *path, char *origname)
854
 
{
855
 
        char *opath = path;
856
 
        struct stat sb;
857
 
 
858
 
        /* check for stdout */
859
 
        if (path[0] == '-' && path[1] == '\0') {
860
 
                handle_stdout(origname);
861
 
                return;
862
 
        }
863
 
 
864
 
        if (stat(path, &sb) != 0) {
865
 
                maybe_warn("can't stat: %s", opath);
866
 
                return;
867
 
        }
868
 
 
869
 
        if (S_ISDIR(sb.st_mode)) {
870
 
                maybe_warnx("%s is a directory", path);
871
 
                return;
872
 
        }
873
 
 
874
 
        if (S_ISREG(sb.st_mode))
875
 
                handle_file(path, strlen(origname) ? origname : basename(path), &sb);
876
 
        else
877
 
                maybe_warnx("%s is not a regular file", path);
878
 
}
879
 
 
880
 
/* compress/decompress a file */
881
 
static void
882
 
handle_file(char *file, char *origname, struct stat *sbp)
883
 
{
884
 
        off_t usize, gsize;
885
 
        char    outfile[PATH_MAX];
886
 
 
887
 
        infile = file;
888
 
        gsize = file_compress(file, origname, outfile, sizeof(outfile));
889
 
        if (gsize == -1)
890
 
                return;
891
 
        usize = sbp->st_size;
892
 
 
893
 
        if (vflag && !tflag)
894
 
                print_verbage(file, (cflag) ? NULL : outfile, usize, gsize);
895
 
}
896
 
 
897
 
/* print a ratio - size reduction as a fraction of uncompressed size */
898
 
static void
899
 
print_ratio(off_t in, off_t out, FILE *where)
900
 
{
901
 
        int percent10;  /* 10 * percent */
902
 
        off_t diff;
903
 
        char buff[8];
904
 
        int len;
905
 
 
906
 
        diff = in - out/2;
907
 
        if (diff <= 0)
908
 
                /*
909
 
                 * Output is more than double size of input! print -99.9%
910
 
                 * Quite possibly we've failed to get the original size.
911
 
                 */
912
 
                percent10 = -999;
913
 
        else {
914
 
                /*
915
 
                 * We only need 12 bits of result from the final division,
916
 
                 * so reduce the values until a 32bit division will suffice.
917
 
                 */
918
 
                while (in > 0x100000) {
919
 
                        diff >>= 1;
920
 
                        in >>= 1;
921
 
                }
922
 
                if (in != 0)
923
 
                        percent10 = ((u_int)diff * 2000) / (u_int)in - 1000;
924
 
                else
925
 
                        percent10 = 0;
926
 
        }
927
 
 
928
 
        len = snprintf(buff, sizeof buff, "%2.2d.", percent10);
929
 
        /* Move the '.' to before the last digit */
930
 
        buff[len - 1] = buff[len - 2];
931
 
        buff[len - 2] = '.';
932
 
        fprintf(where, "%5s%%", buff);
933
 
}
934
 
 
935
 
/* print compression statistics, and the new name (if there is one!) */
936
 
static void
937
 
print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize)
938
 
{
939
 
        if (file)
940
 
                fprintf(stderr, "%s:%s  ", file,
941
 
                    strlen(file) < 7 ? "\t\t" : "\t");
942
 
        print_ratio(usize, gsize, stderr);
943
 
        if (nfile)
944
 
                fprintf(stderr, " -- replaced with %s", nfile);
945
 
        fprintf(stderr, "\n");
946
 
        fflush(stderr);
947
 
}
948
 
 
949
 
/* print a file's info ala --list */
950
 
/* eg:
951
 
  compressed uncompressed  ratio uncompressed_name
952
 
      354841      1679360  78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar
953
 
*/
954
 
static void
955
 
print_list(int fd, off_t out, const char *outfile, time_t ts)
956
 
{
957
 
        static int first = 1;
958
 
        static off_t in_tot, out_tot;
959
 
        uint32_t crc = 0;
960
 
        off_t in = 0, rv;
961
 
 
962
 
        if (first) {
963
 
                if (vflag)
964
 
                        printf("method  crc     date  time  ");
965
 
                if (qflag == 0)
966
 
                        printf("  compressed uncompressed  "
967
 
                               "ratio uncompressed_name\n");
968
 
        }
969
 
        first = 0;
970
 
 
971
 
        /* print totals? */
972
 
        if (fd == -1) {
973
 
                in = in_tot;
974
 
                out = out_tot;
975
 
        } else {
976
 
                /* read the last 4 bytes - this is the uncompressed size */
977
 
                rv = lseek(fd, (off_t)(-8), SEEK_END);
978
 
                if (rv != -1) {
979
 
                        unsigned char buf[8];
980
 
                        uint32_t usize;
981
 
 
982
 
                        rv = read(fd, (char *)buf, sizeof(buf));
983
 
                        if (rv == -1)
984
 
                                maybe_warn("read of uncompressed size");
985
 
                        else if (rv != sizeof(buf))
986
 
                                maybe_warnx("read of uncompressed size");
987
 
 
988
 
                        else {
989
 
                                usize = buf[4] | buf[5] << 8 |
990
 
                                        buf[6] << 16 | buf[7] << 24;
991
 
                                in = (off_t)usize;
992
 
                                crc = buf[0] | buf[1] << 8 |
993
 
                                      buf[2] << 16 | buf[3] << 24;
994
 
                        }
995
 
                }
996
 
        }
997
 
 
998
 
        if (vflag && fd == -1)
999
 
                printf("                            ");
1000
 
        else if (vflag) {
1001
 
                char *date = ctime(&ts);
1002
 
 
1003
 
                /* skip the day, 1/100th second, and year */
1004
 
                date += 4;
1005
 
                date[12] = 0;
1006
 
                printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date);
1007
 
        }
1008
 
        in_tot += in;
1009
 
        out_tot += out;
1010
 
 
1011
 
        printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in);
1012
 
        print_ratio(in, out, stdout);
1013
 
        printf(" %s\n", outfile);
1014
 
}
1015
 
 
1016
 
/* display the usage of NetBSD gzip */
1017
 
static void
1018
 
usage(void)
1019
 
{
1020
 
 
1021
 
        fprintf(stderr, "%s\n", gzip_version);
1022
 
        fprintf(stderr,
1023
 
    "usage: %s [--gnu | --zlib] [-" OPT_LIST "] [<file> [<file> ...]]\n"
1024
 
    " --gnu                    use GNU gzip (/bin/gzip)\n"
1025
 
    " --zlib                   use zlib's implementation (default)\n"
1026
 
    " -1 --fast                fastest (worst) compression\n"
1027
 
    " -2 .. -8                 set compression level\n"
1028
 
    " -9 --best                best (slowest) compression\n"
1029
 
    " -c --stdout              write to stdout, keep original files\n"
1030
 
    "    --to-stdout\n"
1031
 
    " -f --force               force overwriting & compress links\n"
1032
 
    " -l --list                list compressed file contents\n"
1033
 
    " -N --name                save or restore original file name and time stamp\n"
1034
 
    " -n --no-name             don't save original file name or time stamp\n"
1035
 
    " -m --no-timestamp        don't save original time stamp\n"
1036
 
    " -M --force-timestemp     save the timestamp even if -n was passed\n"
1037
 
    " -S .suf                  use suffix .suf instead of .gz\n"
1038
 
    "    --suffix .suf\n"
1039
 
    " -t --test                test compressed file\n"
1040
 
    " -q --quiet               output no warnings\n"
1041
 
    " -V --version             display program version\n"
1042
 
    " -v --verbose             print extra statistics\n"
1043
 
    " -h --help                display this help\n"
1044
 
    " \nzlib-specific options:\n"
1045
 
    " -o NAME\n"
1046
 
    "    --original-name NAME  use NAME as the original file name\n"
1047
 
    " -k --quirk QUIRK         enable a format quirk (buggy-bsd, ntfs)\n"
1048
 
    " -s --osflag              set the OS flag to something different than 03 (Unix)\n",
1049
 
            "gzip");
1050
 
        exit(0);
1051
 
}
1052
 
 
1053
 
/* display the license information of NetBSD gzip */
1054
 
static void
1055
 
display_license(void)
1056
 
{
1057
 
 
1058
 
        fprintf(stderr, "%s\n", gzip_version);
1059
 
        fprintf(stderr, "%s\n", gzip_copyright);
1060
 
        exit(0);
1061
 
}
1062
 
 
1063
 
/* display the version of NetBSD gzip */
1064
 
static void
1065
 
display_version(void)
1066
 
{
1067
 
 
1068
 
        fprintf(stderr, "%s\n", gzip_version);
1069
 
        exit(0);
1070
 
}