diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2003-07-17 20:06:02 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2003-07-17 20:06:02 +0000 |
commit | e110a89f4d58a06bc42084df1a402f1c843e8867 (patch) | |
tree | ca73962090982d16fbcd1889f04d481564c71bf4 | |
parent | a35fbb20dadabd0766b4f955207bd886c40dc456 (diff) |
o implement -l, -n and -N (including setting outfile + mtime)
o make -v behave like GNU gzip for compress/decompress stats
o write a full gzip header w/ mtime and file name
o for -t/-l just don't write data instead of writing to /dev/null
o exit code is now more consistent with GNU gzip
o a crc error on decompress no longer causes unlink(outfile)
mickey@ OK
-rw-r--r-- | usr.bin/compress/compress.1 | 57 | ||||
-rw-r--r-- | usr.bin/compress/compress.h | 33 | ||||
-rw-r--r-- | usr.bin/compress/gzopen.c | 125 | ||||
-rw-r--r-- | usr.bin/compress/main.c | 266 | ||||
-rw-r--r-- | usr.bin/compress/zopen.c | 37 |
5 files changed, 380 insertions, 138 deletions
diff --git a/usr.bin/compress/compress.1 b/usr.bin/compress/compress.1 index 876883feabc..7b7127db825 100644 --- a/usr.bin/compress/compress.1 +++ b/usr.bin/compress/compress.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: compress.1,v 1.20 2003/06/30 03:42:05 millert Exp $ +.\" $OpenBSD: compress.1,v 1.21 2003/07/17 20:06:01 millert Exp $ .\" $NetBSD: compress.1,v 1.5 1995/03/26 09:44:34 glass Exp $ .\" .\" Copyright (c) 1986, 1990, 1993 @@ -47,26 +47,26 @@ .Nm compress .Op Fl LV .Nm compress -.Op Fl cdfghOqrtv123456789 +.Op Fl cdfghlOnNqrtv123456789 .Op Fl b Ar bits .Op Fl S Ar suffix .Op Fl o Ar filename .Op Ar .Nm uncompress -.Op Fl cfhqrtv +.Op Fl cfhlnNqrtv .Op Fl o Ar filename .Op Ar .Pp .Nm gzip .Op Fl LV .Nm gzip -.Op Fl cdfghOqrtv123456789 +.Op Fl cdfghlnNOqrtv123456789 .Op Fl b Ar bits .Op Fl S Ar suffix .Op Fl o Ar filename .Op Ar .Nm gunzip -.Op Fl cfhqrtv +.Op Fl cfhnNqrltv .Op Fl o Ar filename .Op Ar .Pp @@ -165,6 +165,53 @@ This flag need not be specified when invoked as .Nm gzip . .It Fl h Print a short help message. +.It Fl l +List information for the specified compressed files. +The following information is listed: +.Bl -tag -width Ds -offset indent +.It compressed size +size of the compressed file +.It uncompressed size +size of the file when uncompressed +.It compression ratio +ratio of the difference between the compressed and uncompressed +sizes to the uncompressed size. +.It uncompressed name +name the file will be saved as when uncompressing +.El +.Pp +If the +.Fl v +option is specified, the following additional information is printed: +.Bl -tag -width Ds -offset indent +.It compression method +name of the method used to compress the file +.It crc +32-bit crc of the uncompressed file +.It "time stamp" +date and time corresponding to the last data modification time +(mtime) of the compressed file (if the +.Fl n +option is specified, the time stamp stored in the compressed file +is printed instead). +.El +.It Fl n +When compressing, do not save the original file name and time stamp. +This information is saved by default when the deflate scheme is used. +When uncompressing, do not restore the original file name and time stamp. +By default, the uncompressed file inherits the time stamp of the +compressed version and the uncompressed file name is generated by +stripping the +.Dq Z +or +.Dq gz +extension from the compressed file name. +.It Fl N +When compressing, save the original file name and time stamp in the +compressed file. +This information is saved by default when the deflate scheme is used. +When uncompressing or listing, use the time stamp and file name stored +in the compressed file, if any, for the uncompressed version. .It Fl 1...9 Use deflate scheme with compression factor of .Fl 1 diff --git a/usr.bin/compress/compress.h b/usr.bin/compress/compress.h index b9ce25d7ca2..22671b6b436 100644 --- a/usr.bin/compress/compress.h +++ b/usr.bin/compress/compress.h @@ -1,4 +1,4 @@ -/* $OpenBSD: compress.h,v 1.5 2003/07/11 02:31:18 millert Exp $ */ +/* $OpenBSD: compress.h,v 1.6 2003/07/17 20:06:01 millert Exp $ */ /* * Copyright (c) 1997 Michael Shalayeff @@ -27,32 +27,45 @@ * */ +struct z_info { + u_int32_t mtime; /* timestamp */ + u_int32_t crc; /* crc */ + u_int32_t hlen; /* header length */ + u_int64_t total_in; /* # bytes in */ + u_int64_t total_out; /* # bytes out */ +}; + /* * making it any bigger does not affect perfomance very much. * actually this value is just a little bit better than 8192. */ #define Z_BUFSIZE 16384 +/* + * exit codes for compress + */ +#define SUCCESS 0 +#define FAILURE 1 +#define WARNING 2 + extern const char main_rcsid[], z_rcsid[], gz_rcsid[], pkzip_rcsid[], pack_rcsid[], lzh_rcsid[]; -extern int z_check_header(int, struct stat *, const char *); -extern void *z_open(int, const char *, int, int); +extern void *z_open(int, const char *, char *, int, u_int32_t, int); extern FILE *zopen(const char *, const char *,int); extern int zread(void *, char *, int); extern int zwrite(void *, const char *, int); -extern int zclose(void *); +extern int z_close(void *, struct z_info *); + -extern int gz_check_header(int, struct stat *, const char *); -extern void *gz_open(int, const char *, int, int); +extern void *gz_open(int, const char *, char *, int, u_int32_t, int); extern int gz_read(void *, char *, int); extern int gz_write(void *, const char *, int); -extern int gz_close(void *); +extern int gz_close(void *, struct z_info *); extern int gz_flush(void *, int); -extern int lzh_check_header(int, struct stat *, const char *); -extern void *lzh_open(int, const char *, int, int); +extern void *lzh_open(int, const char *, char *, int, u_int32_t, int); extern int lzh_read(void *, char *, int); extern int lzh_write(void *, const char *, int); -extern int lzh_close(void *); +extern int lzh_close(void *, struct z_info *); extern int lzh_flush(void *, int); diff --git a/usr.bin/compress/gzopen.c b/usr.bin/compress/gzopen.c index c7c9e203ad3..d9885fc9205 100644 --- a/usr.bin/compress/gzopen.c +++ b/usr.bin/compress/gzopen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gzopen.c,v 1.12 2003/07/11 02:31:18 millert Exp $ */ +/* $OpenBSD: gzopen.c,v 1.13 2003/07/17 20:06:01 millert Exp $ */ /* * Copyright (c) 1997 Michael Shalayeff @@ -59,10 +59,11 @@ */ const char gz_rcsid[] = - "$OpenBSD: gzopen.c,v 1.12 2003/07/11 02:31:18 millert Exp $"; + "$OpenBSD: gzopen.c,v 1.13 2003/07/17 20:06:01 millert Exp $"; -#include <sys/types.h> +#include <sys/param.h> #include <sys/stat.h> +#include <sys/uio.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -88,6 +89,8 @@ struct gz_stream { z_stream z_stream; /* libz stream */ int z_eof; /* set if end of input file */ u_char z_buf[Z_BUFSIZE]; /* i/o buffer */ + u_int32_t z_time; /* timestamp (mtime) */ + u_int32_t z_hlen; /* length of the gz header */ u_int32_t z_crc; /* crc32 of uncompressed data */ char z_mode; /* 'w' or 'r' */ @@ -97,11 +100,13 @@ static const u_char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ static int put_int32(gz_stream *, u_int32_t); static u_int32_t get_int32(gz_stream *); -static int get_header(gz_stream *, int); +static int get_header(gz_stream *, char *, int); +static int put_header(gz_stream *, char *, u_int32_t); static int get_byte(gz_stream *); void * -gz_open(int fd, const char *mode, int bits, int gotmagic) +gz_open(int fd, const char *mode, char *name, int bits, + u_int32_t mtime, int gotmagic) { gz_stream *s; @@ -124,6 +129,8 @@ gz_open(int fd, const char *mode, int bits, int gotmagic) s->z_stream.avail_in = s->z_stream.avail_out = 0; s->z_fd = 0; s->z_eof = 0; + s->z_time = 0; + s->z_hlen = 0; s->z_crc = crc32(0L, Z_NULL, 0); s->z_mode = mode[0]; @@ -148,22 +155,15 @@ gz_open(int fd, const char *mode, int bits, int gotmagic) s->z_fd = fd; if (s->z_mode == 'w') { - u_char buf[10]; - /* Write a very simple .gz header: */ - buf[0] = gz_magic[0]; - buf[1] = gz_magic[1]; - buf[2] = Z_DEFLATED; - buf[3] = 0 /*flags*/; - buf[4] = buf[5] = buf[6] = buf[7] = 0 /*time*/; - buf[8] = 0 /*xflags*/; - buf[9] = OS_CODE; - if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { - gz_close(s); + /* write the .gz header */ + if (put_header(s, name, mtime) != 0) { + gz_close(s, NULL); s = NULL; } } else { - if (get_header(s, gotmagic) != 0) { /* skip the .gz header */ - gz_close (s); + /* read the .gz header */ + if (get_header(s, name, gotmagic) != 0) { + gz_close(s, NULL); s = NULL; } } @@ -172,7 +172,7 @@ gz_open(int fd, const char *mode, int bits, int gotmagic) } int -gz_close(void *cookie) +gz_close(void *cookie, struct z_info *info) { gz_stream *s = (gz_stream*)cookie; int err = 0; @@ -181,8 +181,11 @@ gz_close(void *cookie) return -1; if (s->z_mode == 'w' && (err = gz_flush (s, Z_FINISH)) == Z_OK) { - if ((err = put_int32 (s, s->z_crc)) == Z_OK) - err = put_int32 (s, s->z_stream.total_in); + if ((err = put_int32 (s, s->z_crc)) == Z_OK) { + s->z_hlen += sizeof(int32_t); + if ((err = put_int32 (s, s->z_stream.total_in)) == Z_OK) + s->z_hlen += sizeof(int32_t); + } } if (!err && s->z_stream.state != NULL) { @@ -192,6 +195,14 @@ gz_close(void *cookie) err = inflateEnd(&s->z_stream); } + if (info != NULL) { + info->mtime = s->z_time; + info->crc = s->z_crc; + info->hlen = s->z_hlen; + info->total_in = (off_t)s->z_stream.total_in; + info->total_out = (off_t)s->z_stream.total_out; + } + if (!err) err = close(s->z_fd); else @@ -282,10 +293,11 @@ get_int32(gz_stream *s) } static int -get_header(gz_stream *s, int gotmagic) +get_header(gz_stream *s, char *name, int gotmagic) { int method; /* method byte */ int flags; /* flags byte */ + char *ep; uInt len; int c; @@ -307,28 +319,49 @@ get_header(gz_stream *s, int gotmagic) return -1; } - /* Discard time, xflags and OS code: */ - for (len = 0; len < 6; len++) - (void)get_byte(s); + /* Stash timestamp (mtime) */ + s->z_time = get_int32(s); + + /* Discard xflags and OS code */ + (void)get_byte(s); + (void)get_byte(s); + s->z_hlen = 10; /* magic, method, flags, time, xflags, OS code */ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ len = (uInt)get_byte(s); len += ((uInt)get_byte(s))<<8; + s->z_hlen += 2; /* len is garbage if EOF but the loop below will quit anyway */ while (len-- != 0 && get_byte(s) != EOF) - ; + s->z_hlen++; } - if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ - while ((c = get_byte(s)) != 0 && c != EOF) ; + if ((flags & ORIG_NAME) != 0) { /* read/save the original file name */ + if ((ep = name) != NULL) + ep += MAXPATHLEN - 1; + while ((c = get_byte(s)) != EOF) { + s->z_hlen++; + if (c == '\0') + break; + if (name < ep) + *name++ = c; + } + if (name != NULL) + *name = '\0'; } if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ - while ((c = get_byte(s)) != 0 && c != EOF) ; + while ((c = get_byte(s)) != EOF) { + s->z_hlen++; + if (c == '\0') + break; + } } if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ - for (len = 0; len < 2; len++) (void)get_byte(s); + for (len = 0; len < 2; len++) + (void)get_byte(s); + s->z_hlen += 2; } if (s->z_eof) { @@ -339,6 +372,36 @@ get_header(gz_stream *s, int gotmagic) return 0; } +static int +put_header(gz_stream *s, char *name, u_int32_t mtime) +{ + struct iovec iov[2]; + u_char buf[10]; + + buf[0] = gz_magic[0]; + buf[1] = gz_magic[1]; + buf[2] = Z_DEFLATED; + buf[3] = name ? ORIG_NAME : 0; + buf[4] = mtime & 0xff; + buf[5] = (mtime >> 8) & 0xff; + buf[6] = (mtime >> 16) & 0xff; + buf[7] = (mtime >> 24) & 0xff; + buf[8] = 0 /* xflags */; + buf[9] = OS_CODE; + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf); + s->z_hlen = sizeof(buf); + + if (name != NULL) { + iov[1].iov_base = name; + iov[1].iov_len = strlen(name) + 1; + s->z_hlen += iov[1].iov_len; + } + if (writev(s->z_fd, iov, name ? 2 : 1) == -1) + return (-1); + return (0); +} + int gz_read(void *cookie, char *buf, int len) { @@ -373,6 +436,7 @@ gz_read(void *cookie, char *buf, int len) errno = EIO; return -1; } + s->z_hlen += 2 * sizeof(int32_t); s->z_eof = 1; break; } @@ -405,4 +469,3 @@ gz_write(void *cookie, const char *buf, int len) return (int)(len - s->z_stream.avail_in); } - diff --git a/usr.bin/compress/main.c b/usr.bin/compress/main.c index 445ad8c0779..94208b39b97 100644 --- a/usr.bin/compress/main.c +++ b/usr.bin/compress/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.36 2003/07/15 19:01:46 millert Exp $ */ +/* $OpenBSD: main.c,v 1.37 2003/07/17 20:06:01 millert Exp $ */ static const char copyright[] = "@(#) Copyright (c) 1992, 1993\n\ @@ -35,7 +35,7 @@ static const char license[] = #if 0 static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; #else -static const char main_rcsid[] = "$OpenBSD: main.c,v 1.36 2003/07/15 19:01:46 millert Exp $"; +static const char main_rcsid[] = "$OpenBSD: main.c,v 1.37 2003/07/17 20:06:01 millert Exp $"; #endif #endif /* not lint */ @@ -47,6 +47,7 @@ static const char main_rcsid[] = "$OpenBSD: main.c,v 1.36 2003/07/15 19:01:46 mi #include <err.h> #include <errno.h> #include <fts.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -59,20 +60,20 @@ static const char main_rcsid[] = "$OpenBSD: main.c,v 1.36 2003/07/15 19:01:46 mi int pipin, force, verbose, testmode, list, nosave; int savename, recurse; -int bits, cat, decomp; +int cat, decomp; extern char *__progname; const struct compressor { char *name; char *suffix; u_char *magic; - void *(*open)(int, const char *, int, int); + void *(*open)(int, const char *, char *, int, u_int32_t, int); int (*read)(void *, char *, int); int (*write)(void *, const char *, int); - int (*close)(void *); + int (*close)(void *, struct z_info *); } c_table[] = { #define M_COMPRESS (&c_table[0]) - { "compress", ".Z", "\037\235", z_open, zread, zwrite, zclose }, + { "compress", ".Z", "\037\235", z_open, zread, zwrite, z_close }, #define M_DEFLATE (&c_table[1]) { "deflate", ".gz", "\037\213", gz_open, gz_read, gz_write, gz_close }, #if 0 @@ -89,13 +90,15 @@ const struct compressor { int permission(const char *); void setfile(const char *, struct stat *); __dead void usage(int); -int compress(const char *, const char *, const struct compressor *, +int compress(const char *, char *, const struct compressor *, int, struct stat *); -int decompress(const char *, const char *, const struct compressor *, +int decompress(const char *, char *, const struct compressor *, int, struct stat *); -const struct compressor *check_method(int, struct stat *, const char *); +const struct compressor *check_method(int); const char *check_suffix(const char *); char *set_outfile(const char *, char *, size_t); +void list_stats(const char *, const struct compressor *, struct z_info *); +void verbose_info(const char *, off_t, off_t, u_int32_t); #define OPTSTRING "123456789ab:cdfghlLnNOo:qrS:tvV" const struct option longopts[] = { @@ -132,7 +135,7 @@ main(int argc, char *argv[]) char *p, *infile; char outfile[MAXPATHLEN], _infile[MAXPATHLEN], suffix[16]; char *nargv[512]; /* some estimate based on ARG_MAX */ - int exists, oreg, ch, error, i, rc, oflag; + int bits, exists, oreg, ch, error, i, rc, oflag; bits = cat = oflag = decomp = 0; p = __progname; @@ -222,12 +225,14 @@ main(int argc, char *argv[]) break; case 'l': list++; + testmode++; + decomp++; break; case 'n': - nosave++; + nosave = 1; break; case 'N': - nosave = 0; /* XXX not yet */ + nosave = -1; break; case 'O': method = M_COMPRESS; @@ -302,8 +307,7 @@ main(int argc, char *argv[]) if ((ftsp = fts_open(argv, FTS_PHYSICAL|FTS_NOCHDIR, 0)) == NULL) err(1, NULL); - /* XXX - set rc in cases where we "continue" below? */ - for (rc = 0; (entry = fts_read(ftsp)) != NULL;) { + for (rc = SUCCESS; (entry = fts_read(ftsp)) != NULL;) { infile = entry->fts_path; switch (entry->fts_info) { case FTS_D: @@ -333,12 +337,13 @@ main(int argc, char *argv[]) case FTS_ERR: case FTS_DNR: warnx("%s: %s", infile, strerror(entry->fts_errno)); - error = 1; + rc = rc ? rc : WARNING; continue; default: if (!S_ISREG(entry->fts_statp->st_mode) && !pipin) { warnx("%s not a regular file%s", infile, cat ? "" : ": unchanged"); + rc = rc ? rc : WARNING; continue; } break; @@ -347,12 +352,11 @@ main(int argc, char *argv[]) if (!decomp && !pipin && (s = check_suffix(infile)) != NULL) { warnx("%s already has %s suffix -- unchanged", infile, s); + rc = rc ? rc : WARNING; continue; } - if (testmode) - strlcpy(outfile, _PATH_DEVNULL, sizeof outfile); - else if (cat) + if (cat) strlcpy(outfile, "/dev/stdout", sizeof outfile); else if (!oflag) { if (decomp) { @@ -375,82 +379,77 @@ main(int argc, char *argv[]) exists = !stat(outfile, &osb); if (!force && exists && S_ISREG(osb.st_mode) && - !permission(outfile)) + !permission(outfile)) { + rc = rc ? rc : WARNING; continue; + } oreg = !exists || S_ISREG(osb.st_mode); - if (verbose > 0) + if (verbose > 0 && !pipin && !list) fprintf(stderr, "%s:\t", infile); error = (decomp ? decompress : compress) (infile, outfile, method, bits, entry->fts_statp); - if (!error && !cat && !testmode && stat(outfile, &osb) == 0) { - if (!force && !decomp && - osb.st_size >= entry->fts_statp->st_size) { - if (verbose > 0) - fprintf(stderr, "file would grow; " - "left unmodified\n"); - error = 1; - rc = rc ? rc : 2; - } else { + switch (error) { + case SUCCESS: + if (!cat && !testmode) { setfile(outfile, entry->fts_statp); - - if (unlink(infile) && verbose >= 0) + if (!pipin && unlink(infile) && verbose >= 0) warn("input: %s", infile); - - if (verbose > 0) { - u_int ratio; - ratio = (1000 * osb.st_size) - / entry->fts_statp->st_size; - fprintf(stderr, "%u", ratio / 10); - if (ratio % 10) - fprintf(stderr, ".%u", - ratio % 10); - fputc('%', stderr); - fputc(' ', stderr); - } } + break; + case WARNING: + rc = rc ? rc : WARNING; + break; + default: + rc = FAILURE; + if (oreg && unlink(outfile) && errno != ENOENT && + verbose >= 0) { + if (force) + warn("output: %s", outfile); + else + err(1, "output: %s", outfile); + } + break; } - - if (error > 0 && oreg && unlink(outfile) && errno != ENOENT && - verbose >= 0) { - if (force) { - warn("output: %s", outfile); - rc = 1; - } else - err(1, "output: %s", outfile); - } else if (!error && verbose > 0) - fputs("OK\n", stderr); } + if (list) + list_stats(NULL, NULL, NULL); exit(rc); } int -compress(const char *in, const char *out, const struct compressor *method, +compress(const char *in, char *out, const struct compressor *method, int bits, struct stat *sb) { u_char buf[Z_BUFSIZE]; - int error, ifd, ofd; + char *name; + int error, ifd, ofd, flags; void *cookie; ssize_t nr; + u_int32_t mtime; + struct z_info info; - error = 0; + mtime = 0; + flags = 0; + error = SUCCESS; + name = NULL; cookie = NULL; if ((ifd = open(in, O_RDONLY)) < 0) { if (verbose >= 0) warn("%s", out); - return (-1); + return (FAILURE); } if ((ofd = open(out, O_WRONLY|O_CREAT, S_IWUSR)) < 0) { if (verbose >= 0) warn("%s", out); (void) close(ifd); - return (-1); + return (FAILURE); } if (method != M_COMPRESS && !force && isatty(ofd)) { @@ -459,48 +458,61 @@ compress(const char *in, const char *out, const struct compressor *method, out); (void) close(ofd); (void) close(ifd); - return (-1); + return (FAILURE); } - if ((cookie = (*method->open)(ofd, "w", bits, 0)) == NULL) { + if (!pipin && nosave <= 0) { + name = basename(in); + mtime = (u_int32_t)sb->st_mtime; + } + if ((cookie = (*method->open)(ofd, "w", name, bits, mtime, flags)) == NULL) { if (verbose >= 0) warn("%s", in); (void) close(ofd); (void) close(ifd); - return (-1); + return (FAILURE); } while ((nr = read(ifd, buf, sizeof(buf))) > 0) if ((method->write)(cookie, buf, nr) != nr) { if (verbose >= 0) warn("%s", out); - error++; + error = FAILURE; break; } if (!error && nr < 0) { if (verbose >= 0) warn("%s", in); - error++; + error = FAILURE; } - if ((method->close)(cookie)) { + if ((method->close)(cookie, &info)) { if (!error && verbose >= 0) warn("%s", out); - error++; + error = FAILURE; } if (close(ifd)) { if (!error && verbose >= 0) warn("%s", in); - error++; + error = FAILURE; } + + if (!force && info.total_out >= info.total_in) { + if (verbose > 0) + fprintf(stderr, "file would grow; left unmodified\n"); + error = WARNING; + } + + if (!error && verbose > 0) + verbose_info(out, info.total_out, info.total_in, info.hlen); return (error); } const struct compressor * -check_method(int fd, struct stat *sb, const char *out) +check_method(int fd) { const struct compressor *method; u_char magic[2]; @@ -516,15 +528,16 @@ check_method(int fd, struct stat *sb, const char *out) } int -decompress(const char *in, const char *out, const struct compressor *method, +decompress(const char *in, char *out, const struct compressor *method, int bits, struct stat *sb) { u_char buf[Z_BUFSIZE]; int error, ifd, ofd; void *cookie; ssize_t nr; + struct z_info info; - error = 0; + error = SUCCESS; cookie = NULL; if ((ifd = open(in, O_RDONLY)) < 0) { @@ -541,53 +554,73 @@ decompress(const char *in, const char *out, const struct compressor *method, return -1; } - if ((method = check_method(ifd, sb, out)) == NULL) { + if ((method = check_method(ifd)) == NULL) { if (verbose >= 0) warnx("%s: unrecognized file format", in); close (ifd); return -1; } - if ((cookie = (*method->open)(ifd, "r", bits, 1)) == NULL) { + /* XXX - open constrains outfile to MAXPATHLEN so this is safe */ + if ((cookie = (*method->open)(ifd, "r", nosave < 0 ? out : NULL, + bits, 0, 1)) == NULL) { if (verbose >= 0) warn("%s", in); - error++; close (ifd); - return -1; + return (FAILURE); } - if ((ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR)) < 0) { + if (testmode) + ofd = -1; + else if ((ofd = open(out, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR)) < 0) { if (verbose >= 0) warn("%s", in); - (method->close)(cookie); - return -1; + (method->close)(cookie, NULL); + return (FAILURE); } - while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0) - if (write(ofd, buf, nr) != nr) { + while ((nr = (method->read)(cookie, buf, sizeof(buf))) > 0) { + if (ofd != -1 && write(ofd, buf, nr) != nr) { if (verbose >= 0) warn("%s", out); - error++; + error = FAILURE; break; } + } if (!error && nr < 0) { if (verbose >= 0) warnx("%s: %s", in, errno == EINVAL ? "crc error" : strerror(errno)); - error++; + error = errno == EINVAL ? WARNING : FAILURE; } - if ((method->close)(cookie)) { + if ((method->close)(cookie, &info)) { if (!error && verbose >= 0) warnx("%s", in); - error++; + error = FAILURE; } - if (close(ofd)) { + if (nosave < 0) { + sb->st_mtimespec.tv_sec = info.mtime; + sb->st_mtimespec.tv_nsec = 0; + } + + if (ofd != -1 && close(ofd)) { if (!error && verbose >= 0) warn("%s", out); - error++; + error = FAILURE; + } + + if (!error) { + if (list) { + if (info.mtime == 0) + info.mtime = (u_int32_t)sb->st_mtime; + list_stats(pipin ? "stdout" : out, method, &info); + } else if (verbose > 0) { + verbose_info(out, info.total_in, info.total_out, + info.hlen); + } } return (error); @@ -688,6 +721,71 @@ set_outfile(const char *infile, char *outfile, size_t osize) return (outfile); } +/* + * Print output for the -l option. + */ +void +list_stats(const char *name, const struct compressor *method, + struct z_info *info) +{ + static off_t compressed_total, uncompressed_total, header_total; + static u_int nruns; + char *timestr; + + if (nruns == 0) { + if (verbose >= 0) { + if (verbose > 0) + fputs("method crc date time ", stdout); + puts("compressed uncompr. ratio uncompressed_name"); + } + } + nruns++; + + if (name != NULL) { + if (verbose > 0) { + timestr = ctime(&info->mtime) + 4; + timestr[12] = '\0'; + printf("%.5s %08x %s ", method->name, info->crc, timestr); + } + printf("%9lld %9lld %4.1f%% %s\n", + (long long)(info->total_in + info->hlen), + (long long)info->total_out, + (info->total_out - info->total_in) * + 100.0 / info->total_out, name); + compressed_total += info->total_in; + uncompressed_total += info->total_out; + header_total += info->hlen; + } else if (verbose >= 0) { + if (nruns < 3) /* only do totals for > 1 files */ + return; + if (verbose > 0) + fputs(" ", stdout); + printf("%9lld %9lld %4.1f%% (totals)\n", + (long long)(compressed_total + header_total), + (long long)uncompressed_total, + (uncompressed_total - compressed_total) * + 100.0 / uncompressed_total); + } +} + +void +verbose_info(const char *file, off_t compressed, off_t uncompressed, + u_int32_t hlen) +{ + if (testmode) { + fputs("OK\n", stderr); + return; + } + if (!pipin) { + fprintf(stderr, "\t%4.1f%% -- replaced with %s\n", + (uncompressed - compressed) * 100.0 / uncompressed, file); + } + compressed += hlen; + fprintf(stderr, "%lld bytes in, %lld bytes out\n", + (long long)(decomp ? compressed : uncompressed), + (long long)(decomp ? uncompressed : compressed)); +} + __dead void usage(int status) { diff --git a/usr.bin/compress/zopen.c b/usr.bin/compress/zopen.c index 9b26edcb206..058a9008350 100644 --- a/usr.bin/compress/zopen.c +++ b/usr.bin/compress/zopen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: zopen.c,v 1.12 2003/07/11 02:31:18 millert Exp $ */ +/* $OpenBSD: zopen.c,v 1.13 2003/07/17 20:06:01 millert Exp $ */ /* $NetBSD: zopen.c,v 1.5 1995/03/26 09:44:53 glass Exp $ */ /*- @@ -40,7 +40,7 @@ static char sccsid[] = "@(#)zopen.c 8.1 (Berkeley) 6/27/93"; #else const char z_rcsid[] = - "$OpenBSD: zopen.c,v 1.12 2003/07/11 02:31:18 millert Exp $"; + "$OpenBSD: zopen.c,v 1.13 2003/07/17 20:06:01 millert Exp $"; #endif /*- @@ -123,7 +123,7 @@ struct s_zstate { long zs_ratio; count_int zs_checkpoint; long zs_in_count; /* Length of input. */ - long zs_bytes_out; /* Length of compressed output. */ + long zs_bytes_out; /* Length of output. */ long zs_out_count; /* # of codes output (for debugging).*/ u_char zs_buf[ZBUFSIZ]; /* I/O buffer */ u_char *zs_bp; /* Current I/O window in the zs_buf */ @@ -313,7 +313,7 @@ nomatch: if (output(zs, (code_int) zs->zs_ent) == -1) } int -zclose(void *cookie) +z_close(void *cookie, struct z_info *info) { struct s_zstate *zs; int rval; @@ -332,11 +332,26 @@ zclose(void *cookie) return (-1); } } + + if (info != NULL) { + info->mtime = 0; + info->crc = (u_int32_t)-1; + info->hlen = 0; + info->total_in = (off_t)zs->zs_in_count; + info->total_out = (off_t)zs->zs_bytes_out; + } + rval = close(zs->zs_fd); free(zs); return (rval); } +int +zclose(void *cookie) +{ + return z_close(cookie, NULL); +} + /*- * Output the given code. * Inputs: @@ -493,6 +508,7 @@ zread(void *cookie, char *rbp, int num) return (-1); } zs->zs_maxbits = header[2]; /* Set -b from file. */ + zs->zs_in_count += sizeof(header); zs->zs_block_compress = zs->zs_maxbits & BLOCK_MASK; zs->zs_maxbits &= BIT_MASK; zs->zs_maxmaxcode = 1L << zs->zs_maxbits; @@ -545,8 +561,10 @@ zread(void *cookie, char *rbp, int num) /* And put them out in forward order. */ middle: do { - if (count-- == 0) + if (count-- == 0) { + zs->zs_bytes_out += num; return (num); + } *bp++ = *--zs->zs_stackp; } while (zs->zs_stackp > de_stack); @@ -561,6 +579,7 @@ middle: do { zs->zs_oldcode = zs->zs_incode; } zs->zs_state = S_EOF; + zs->zs_bytes_out += num - count; eof: return (num - count); } @@ -607,6 +626,7 @@ getcode(struct s_zstate *zs) if ((bits = read(zs->zs_fd, bp, ZBUFSIZ - (bp - zs->zs_buf))) < 0) return -1; + zs->zs_in_count += bits; zs->zs_bp = zs->zs_buf; zs->zs_ebp = bp + bits; } @@ -717,7 +737,7 @@ zopen(const char *name, const char *mode, int bits) if ((fd = open(name, (*mode=='r'? O_RDONLY:O_WRONLY|O_CREAT), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) return NULL; - if ((cookie = z_open(fd, mode, bits, 0)) == NULL) { + if ((cookie = z_open(fd, mode, NULL, bits, 0, 0)) == NULL) { close(fd); return NULL; } @@ -726,7 +746,8 @@ zopen(const char *name, const char *mode, int bits) } void * -z_open(int fd, const char *mode, int bits, int gotmagic) +z_open(int fd, const char *mode, char *name, int bits, + u_int32_t mtime, int gotmagic) { struct s_zstate *zs; @@ -749,7 +770,7 @@ z_open(int fd, const char *mode, int bits, int gotmagic) zs->zs_clear_flg = 0; zs->zs_ratio = 0; zs->zs_checkpoint = CHECK_GAP; - zs->zs_in_count = 1; /* Length of input. */ + zs->zs_in_count = 0; /* Length of input. */ zs->zs_out_count = 0; /* # of codes output (for debugging).*/ zs->zs_state = gotmagic ? S_MAGIC : S_START; zs->zs_offset = 0; |