compress.c revision 3d63ea05cb8474d8036d3588cf8299306a994b8c
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Compress - data compression program
*/
#define min(a, b) ((a > b) ? b : a)
/*
* machine variants which require cc -Dmachine: pdp11, z8000, pcxt
*/
/*
* Set USERMEM to the maximum amount of physical user memory available
* in bytes. USERMEM is used to determine the maximum BITS that can be used
* for compression.
*
* SACREDMEM is the amount of physical memory saved for others; compress
* will hog the rest.
*/
#ifndef SACREDMEM
#define SACREDMEM 0
#endif
#ifndef USERMEM
#define USERMEM 450000 /* default user memory */
#endif
#ifdef USERMEM
#if USERMEM >= (433484+SACREDMEM)
#define PBITS 16
#else
#if USERMEM >= (229600+SACREDMEM)
#define PBITS 15
#else
#if USERMEM >= (127536+SACREDMEM)
#define PBITS 14
#else
#if USERMEM >= (73464+SACREDMEM)
#define PBITS 13
#else
#define PBITS 12
#endif
#endif
#endif
#endif
#undef USERMEM
#endif /* USERMEM */
#ifdef PBITS /* Preferred BITS for this memory size */
#ifndef BITS
#define BITS PBITS
#endif /* BITS */
#endif /* PBITS */
#if BITS == 16
#define HSIZE 69001 /* 95% occupancy */
#endif
#if BITS == 15
#define HSIZE 35023 /* 94% occupancy */
#endif
#if BITS == 14
#define HSIZE 18013 /* 91% occupancy */
#endif
#if BITS == 13
#define HSIZE 9001 /* 91% occupancy */
#endif
#if BITS <= 12
#define HSIZE 5003 /* 80% occupancy */
#endif
#define OUTSTACKSIZE (2<<BITS)
/*
* a code_int must be able to hold 2**BITS values of type int, and also -1
*/
#if BITS > 15
typedef long int code_int;
#else
typedef int code_int;
#endif
typedef long int count_int;
typedef long long count_long;
typedef unsigned char char_type;
static char_type magic_header[] = { "\037\235" }; /* 1F 9D */
/* Defines for third byte of header */
#define BIT_MASK 0x1f
#define BLOCK_MASK 0x80
/*
* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is
* a fourth header byte(for expansion).
*/
#define INIT_BITS 9 /* initial number of bits/code */
/*
* compress.c - File compression ala IEEE Computer, June 1984.
*/
static char rcs_ident[] =
"$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $";
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/param.h>
#include <stdlib.h> /* XCU4 */
#include <limits.h>
#include <libintl.h>
#include <locale.h>
#include <langinfo.h>
#include <string.h>
#include <sys/acl.h>
#include <utime.h>
#include <libgen.h>
#include <setjmp.h>
#include <strings.h>
#include <fcntl.h>
#include <dirent.h>
#include <aclutils.h>
#include <errno.h>
#include "getresponse.h"
static int n_bits; /* number of bits/code */
static int maxbits = BITS; /* user settable max # bits/code */
static code_int maxcode; /* maximum code, given n_bits */
/* should NEVER generate this code */
static code_int maxmaxcode = 1 << BITS;
#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
static count_int htab [OUTSTACKSIZE];
static unsigned short codetab [OUTSTACKSIZE];
#define htabof(i) htab[i]
#define codetabof(i) codetab[i]
static code_int hsize = HSIZE; /* for dynamic table sizing */
static off_t fsize; /* file size of input file */
/*
* To save much memory, we overlay the table used by compress() with those
* used by decompress(). The tab_prefix table is the same size and type
* as the codetab. The tab_suffix table needs 2**BITS characters. We
* get this from the beginning of htab. The output stack uses the rest
* of htab, and contains characters. There is plenty of room for any
* possible stack (stack used to be 8000 characters).
*/
#define tab_prefixof(i) codetabof(i)
#define tab_suffixof(i) ((char_type *)(htab))[i]
#define de_stack ((char_type *)&tab_suffixof(1<<BITS))
#define stack_max ((char_type *)&tab_suffixof(OUTSTACKSIZE))
static code_int free_ent = 0; /* first unused entry */
static int newline_needed = 0;
static int didnt_shrink = 0;
static int perm_stat = 0; /* permanent status */
static code_int getcode();
/* Use a 3-byte magic number header, unless old file */
static int nomagic = 0;
/* Write output on stdout, suppress messages */
static int zcat_flg = 0; /* use stdout on all files */
static int zcat_cmd = 0; /* zcat cmd */
static int use_stdout = 0; /* set for each file processed */
/* Don't unlink output file on interrupt */
static int precious = 1;
static int quiet = 1; /* don't tell me about compression */
/*
* block compression parameters -- after all codes are used up,
* and compression rate changes, start over.
*/
static int block_compress = BLOCK_MASK;
static int clear_flg = 0;
static long int ratio = 0;
#define CHECK_GAP 10000 /* ratio check interval */
static count_long checkpoint = CHECK_GAP;
/*
* the next two codes should not be changed lightly, as they must not
* lie within the contiguous general code space.
*/
#define FIRST 257 /* first free entry */
#define CLEAR 256 /* table clear output code */
static int force = 0;
static char ofname [MAXPATHLEN];
static int Vflg = 0;
static int vflg = 0;
static int qflg = 0;
static int bflg = 0;
static int Fflg = 0;
static int dflg = 0;
static int cflg = 0;
static int Cflg = 0;
#ifdef DEBUG
int verbose = 0;
int debug = 0;
#endif /* DEBUG */
static void (*oldint)();
static int bgnd_flag;
static int do_decomp = 0;
static char *progname;
static char *optstr;
/*
* Fix lint errors
*/
static char *local_basename(char *);
static int addDotZ(char *, size_t);
static void Usage(void);
static void cl_block(count_long);
static void cl_hash(count_int);
static void compress(void);
static void copystat(char *, struct stat *, char *);
static void decompress(void);
static void ioerror(void);
static void onintr();
static void oops();
static void output(code_int);
static void prratio(FILE *, count_long, count_long);
static void version(void);
static int mv_xattrs(char *, char *, int);
#ifdef DEBUG
static int in_stack(int, int);
static void dump_tab(void);
static void printcodes(void);
#endif
/* For error-handling */
static jmp_buf env;
/* For input and ouput */
static FILE *inp; /* the current input file */
static FILE *infile; /* disk-based input stream */
static FILE *outp; /* current output file */
static FILE *outfile; /* disk-based output stream */
/* For output() */
static char buf[BITS];
static char_type lmask[9] =
{0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
static char_type rmask[9] =
{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
/* For compress () */
static int offset;
static count_long bytes_out; /* length of compressed output */
/* # of codes output (for debugging) */
/* For dump_tab() */
#define STACK_SIZE 15000
#ifdef DEBUG
code_int sorttab[1<<BITS]; /* sorted pointers into htab */
#endif
/*
* *************************************************************
* TAG( main )
*
* Algorithm from "A Technique for High Performance Data Compression",
* Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19.
*
* Usage: compress [-dfvc] [-b bits] [file ...]
* Inputs:
* -d: If given, decompression is done instead.
*
* -c: Write output on stdout, don't remove original.
*
* -b: Parameter limits the max number of bits/code.
*
* -f: Forces output file to be generated, even if one already
* exists, and even if no space is saved by compressing.
* If -f is not used, the user will be prompted if stdin is
* a tty, otherwise, the output file will not be overwritten.
*
* -v: Write compression statistics
*
* file ...: Files to be compressed. If none specified, stdin
* is used.
* Outputs:
* file.Z: Compressed form of file with same mode, owner, and utimes
* or stdout (if stdin used as input)
*
* Assumptions:
* When filenames are given, replaces with the compressed version
* (.Z suffix) only if the file decreases in size.
* Algorithm:
* Modified Lempel-Ziv method (LZW). Basically finds common
* substrings and replaces them with a variable size code. This is
* deterministic, and can be done on the fly. Thus, the decompression
* procedure needs no input table, but tracks the way the table was built.
*/
int
main(int argc, char *argv[])
{
int overwrite = 0; /* Do not overwrite unless given -f flag */
char tempname[MAXPATHLEN];
char line[LINE_MAX];
char **filelist, **fileptr;
char *cp;
struct stat statbuf;
struct stat ostatbuf;
int ch; /* XCU4 */
char *p;
extern int optind, optopt;
extern char *optarg;
int dash_count = 0; /* times "-" is on cmdline */
/* XCU4 changes */
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
#endif
(void) textdomain(TEXT_DOMAIN);
if (init_yes() < 0) {
(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
strerror(errno));
exit(1);
}
/* This bg check only works for sh. */
if ((oldint = signal(SIGINT, SIG_IGN)) != SIG_IGN) {
(void) signal(SIGINT, onintr);
(void) signal(SIGSEGV, oops);
}
bgnd_flag = oldint != SIG_DFL;
/* Allocate room for argv + "-" (if stdin needs to be added) */
filelist = fileptr = (char **)(malloc((argc + 1) * sizeof (*argv)));
*filelist = NULL;
if ((cp = rindex(argv[0], '/')) != 0) {
cp++;
} else {
cp = argv[0];
}
if (strcmp(cp, "uncompress") == 0) {
do_decomp = 1;
} else if (strcmp(cp, "zcat") == 0) {
do_decomp = 1;
zcat_cmd = zcat_flg = 1;
}
progname = local_basename(argv[0]);
/*
* Argument Processing
* All flags are optional.
* -D = > debug
* -V = > print Version; debug verbose
* -d = > do_decomp
* -v = > unquiet
* -f = > force overwrite of output file
* -n = > no header: useful to uncompress old files
* -b maxbits => maxbits. If -b is specified,
* then maxbits MUST be given also.
* -c = > cat all output to stdout
* -C = > generate output compatible with compress 2.0.
* if a string is left, must be an input filename.
*/
#ifdef DEBUG
optstr = "b:cCdDfFnqvV";
#else
optstr = "b:cCdfFnqvV";
#endif
while ((ch = getopt(argc, argv, optstr)) != EOF) {
/* Process all flags in this arg */
switch (ch) {
#ifdef DEBUG
case 'D':
debug = 1;
break;
case 'V':
verbose = 1;
version();
break;
#else
case 'V':
version();
Vflg++;
break;
#endif /* DEBUG */
case 'v':
quiet = 0;
vflg++;
break;
case 'd':
do_decomp = 1;
dflg++;
break;
case 'f':
case 'F':
Fflg++;
overwrite = 1;
force = 1;
break;
case 'n':
nomagic = 1;
break;
case 'C':
Cflg++;
block_compress = 0;
break;
case 'b':
bflg++;
p = optarg;
if (!p) {
(void) fprintf(stderr, gettext(
"Missing maxbits\n"));
Usage();
exit(1);
}
maxbits = strtoul(optarg, &p, 10);
if (*p) {
(void) fprintf(stderr, gettext(
"Missing maxbits\n"));
Usage();
exit(1);
}
break;
case 'c':
cflg++;
zcat_flg = 1;
break;
case 'q':
qflg++;
quiet = 1;
break;
default:
(void) fprintf(stderr, gettext(
"Unknown flag: '%c'\n"), optopt);
Usage();
exit(1);
}
} /* while */
/*
* Validate zcat syntax
*/
if (zcat_cmd && (Fflg | Cflg | cflg |
bflg | qflg | dflg | nomagic)) {
(void) fprintf(stderr, gettext(
"Invalid Option\n"));
Usage();
exit(1);
}
/*
* Process the file list
*/
for (; optind < argc; optind++) {
if (strcmp(argv[optind], "-") == 0) {
dash_count++;
}
*fileptr++ = argv[optind]; /* Build input file list */
*fileptr = NULL;
}
if (dash_count > 1) {
(void) fprintf(stderr,
gettext("%s may only appear once in the file"
" list\n"), "\"-\"");
exit(1);
}
if (fileptr - filelist == 0) {
*fileptr++ = "-";
*fileptr = NULL;
}
if (fileptr - filelist > 1 && cflg && !do_decomp) {
(void) fprintf(stderr,
gettext("compress: only one file may be compressed"
" to stdout\n"));
exit(1);
}
if (maxbits < INIT_BITS)
maxbits = INIT_BITS;
if (maxbits > BITS)
maxbits = BITS;
maxmaxcode = 1 << maxbits;
/* Need to open something to close with freopen later */
if ((infile = fopen("/dev/null", "r")) == NULL) {
(void) fprintf(stderr, gettext("Error opening /dev/null for "
"input\n"));
exit(1);
}
if ((outfile = fopen("/dev/null", "w")) == NULL) {
(void) fprintf(stderr, gettext("Error opening /dev/null for "
"output\n"));
exit(1);
}
for (fileptr = filelist; *fileptr; fileptr++) {
int jmpval = 0;
didnt_shrink = 0;
newline_needed = 0;
if (do_decomp) {
/* DECOMPRESSION */
if (strcmp(*fileptr, "-") == 0) {
/* process stdin */
inp = stdin;
outp = stdout;
use_stdout = 1;
*fileptr = "stdin"; /* for error messages */
} else {
/* process the named file */
inp = infile;
outp = outfile;
use_stdout = 0;
if (zcat_flg) {
use_stdout = 1;
outp = stdout;
}
/* Check for .Z suffix */
if (strcmp(*fileptr +
strlen(*fileptr) - 2, ".Z") != 0) {
/* No .Z: tack one on */
if (strlcpy(tempname, *fileptr,
sizeof (tempname)) >=
sizeof (tempname)) {
(void) fprintf(stderr,
gettext("%s: filename "
"too long\n"),
*fileptr);
perm_stat = 1;
continue;
}
if (addDotZ(tempname,
sizeof (tempname)) < 0) {
perm_stat = 1;
continue;
}
*fileptr = tempname;
}
/* Open input file */
if (stat(*fileptr, &statbuf) < 0) {
perror(*fileptr);
perm_stat = 1;
continue;
}
if ((freopen(*fileptr, "r", inp)) == NULL) {
perror(*fileptr);
perm_stat = 1;
continue;
}
}
/* Check the magic number */
if (nomagic == 0) {
if ((getc(inp) !=
(magic_header[0] & 0xFF)) ||
(getc(inp) !=
(magic_header[1] & 0xFF))) {
(void) fprintf(stderr, gettext(
"%s: not in compressed "
"format\n"),
*fileptr);
perm_stat = 1;
continue;
}
/* set -b from file */
if ((maxbits = getc(inp)) == EOF &&
ferror(inp)) {
perror(*fileptr);
perm_stat = 1;
continue;
}
block_compress = maxbits & BLOCK_MASK;
maxbits &= BIT_MASK;
maxmaxcode = 1 << maxbits;
if (maxbits > BITS) {
(void) fprintf(stderr,
gettext("%s: compressed "
"with %d bits, "
"can only handle"
" %d bits\n"),
*fileptr, maxbits, BITS);
perm_stat = 1;
continue;
}
}
if (!use_stdout) {
/* Generate output filename */
if (strlcpy(ofname, *fileptr,
sizeof (ofname)) >=
sizeof (ofname)) {
(void) fprintf(stderr,
gettext("%s: filename "
"too long\n"),
*fileptr);
perm_stat = 1;
continue;
}
/* Strip off .Z */
ofname[strlen(*fileptr) - 2] = '\0';
}
} else {
/* COMPRESSION */
if (strcmp(*fileptr, "-") == 0) {
/* process stdin */
inp = stdin;
outp = stdout;
use_stdout = 1;
*fileptr = "stdin"; /* for error messages */
/* Use the largest possible hash table */
hsize = HSIZE;
} else {
/* process the named file */
inp = infile;
outp = outfile;
use_stdout = 0;
if (zcat_flg) {
use_stdout = 1;
outp = stdout;
}
if (strcmp(*fileptr +
strlen(*fileptr) - 2, ".Z") == 0) {
(void) fprintf(stderr, gettext(
"%s: already has .Z "
"suffix -- no change\n"),
*fileptr);
perm_stat = 1;
continue;
}
/* Open input file */
if (stat(*fileptr, &statbuf) < 0) {
perror(*fileptr);
perm_stat = 1;
continue;
}
if ((freopen(*fileptr, "r", inp)) == NULL) {
perror(*fileptr);
perm_stat = 1;
continue;
}
fsize = (off_t)statbuf.st_size;
/*
* tune hash table size for small
* files -- ad hoc,
* but the sizes match earlier #defines, which
* serve as upper bounds on the number of
* output codes.
*/
hsize = HSIZE;
if (fsize < (1 << 12))
hsize = min(5003, HSIZE);
else if (fsize < (1 << 13))
hsize = min(9001, HSIZE);
else if (fsize < (1 << 14))
hsize = min(18013, HSIZE);
else if (fsize < (1 << 15))
hsize = min(35023, HSIZE);
else if (fsize < 47000)
hsize = min(50021, HSIZE);
if (!use_stdout) {
/* Generate output filename */
if (strlcpy(ofname, *fileptr,
sizeof (ofname)) >=
sizeof (ofname)) {
(void) fprintf(stderr,
gettext("%s: filename "
"too long\n"),
*fileptr);
perm_stat = 1;
continue;
}
if (addDotZ(ofname,
sizeof (ofname)) < 0) {
perm_stat = 1;
continue;
}
}
}
} /* if (do_decomp) */
/* Check for overwrite of existing file */
if (!overwrite && !use_stdout) {
if (stat(ofname, &ostatbuf) == 0) {
(void) fprintf(stderr, gettext(
"%s already exists;"), ofname);
if (bgnd_flag == 0 && isatty(2)) {
int cin;
(void) fprintf(stderr, gettext(
" do you wish to overwr"
"ite %s (%s or %s)? "),
ofname, yesstr, nostr);
(void) fflush(stderr);
for (cin = 0; cin < LINE_MAX; cin++)
line[cin] = 0;
(void) read(2, line, LINE_MAX);
if (yes_check(line) == 0) {
(void) fprintf(stderr,
gettext(
"\tnot overwri"
"tten\n"));
continue;
}
} else {
/*
* XPG4: Assertion 1009
* Standard input is not
* terminal, and no '-f',
* and file exists.
*/
(void) fprintf(stderr, gettext(
"%s: File exists, -f not"
" specified, and ru"
"nning in the backgro"
"und.\n"), *fileptr);
perm_stat = 1;
continue;
}
}
}
if (!use_stdout) {
if (pathconf(ofname, _PC_XATTR_EXISTS) == 1) {
(void) unlink(ofname);
}
/* Open output file */
if (freopen(ofname, "w", outp) == NULL) {
perror(ofname);
perm_stat = 1;
continue;
}
precious = 0;
if (!quiet) {
(void) fprintf(stderr, "%s: ",
*fileptr);
newline_needed = 1;
}
} else if (!quiet && !do_decomp) {
(void) fprintf(stderr, "%s: ",
*fileptr);
newline_needed = 1;
}
/* Actually do the compression/decompression */
if ((jmpval = setjmp(env)) == 0) {
/* We'll see how things go */
#ifndef DEBUG
if (do_decomp == 0) {
compress();
} else {
decompress();
}
#else
if (do_decomp == 0) {
compress();
} else if (debug == 0) {
decompress();
} else {
printcodes();
}
if (verbose) {
dump_tab();
}
#endif
} else {
/*
* Things went badly - clean up and go on.
* jmpval's values break down as follows:
* 1 == message determined by ferror() values.
* 2 == input problem message needed.
* 3 == output problem message needed.
*/
if (ferror(inp) || jmpval == 2) {
if (do_decomp) {
(void) fprintf(stderr, gettext(
"uncompress: %s: corrupt"
" input\n"), *fileptr);
} else {
perror(*fileptr);
}
}
if (ferror(outp) || jmpval == 3) {
/* handle output errors */
if (use_stdout) {
perror("");
} else {
perror(ofname);
}
}
if (ofname[0] != '\0') {
if (unlink(ofname) < 0) {
perror(ofname);
}
ofname[0] = '\0';
}
perm_stat = 1;
continue;
}
/* Things went well */
if (!use_stdout) {
/* Copy stats */
copystat(*fileptr, &statbuf, ofname);
precious = 1;
if (newline_needed) {
(void) putc('\n', stderr);
}
/*
* Print the info. for unchanged file
* when no -v
*/
if (didnt_shrink) {
if (!force && perm_stat == 0) {
if (quiet) {
(void) fprintf(stderr, gettext(
"%s: -- file "
"unchanged\n"),
*fileptr);
}
perm_stat = 2;
}
}
} else {
if (didnt_shrink && !force && perm_stat == 0) {
perm_stat = 2;
}
if (newline_needed) {
(void) fprintf(stderr, "\n");
}
}
} /* for */
return (perm_stat);
}
static void
cinterr(int hshift)
{
/* we have exceeded the hash table */
(void) fprintf(stderr,
"internal error: hashtable exceeded - hsize = %ld\n", hsize);
(void) fprintf(stderr, "hshift = %d, %d\n", hshift, (1 << hshift) -1);
(void) fprintf(stderr, "maxbits = %d\n", maxbits);
(void) fprintf(stderr, "n_bits = %d\n", n_bits);
(void) fprintf(stderr, "maxcode = %ld\n", maxcode);
longjmp(env, 1);
}
static code_int
adjusti(code_int i, code_int hsize_reg)
{
while (i < 0) {
i += hsize_reg;
}
while (i >= hsize_reg) {
i -= hsize_reg;
}
return (i);
}
/*
* compress inp to outp
*
* Algorithm: use open addressing double hashing(no chaining) on the
* prefix code / next character combination. We do a variant of Knuth's
* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
* secondary probe. Here, the modular division first probe is gives way
* to a faster exclusive-or manipulation. Also do block compression with
* an adaptive reset, whereby the code table is cleared when the compression
* ratio decreases, but after the table fills. The variable-length output
* codes are re-sized at this point, and a special CLEAR code is generated
* for the decompressor. Late addition: construct the table according to
* file size for noticeable speed improvement on small files. Please direct
* questions about this implementation to ames!jaw.
*/
static void
compress()
{
long fcode;
code_int i = 0;
int c;
code_int ent;
int disp;
code_int hsize_reg;
int hshift;
int probecnt;
count_long in_count;
uint32_t inchi, inclo;
int maxbits_reg;
FILE *fin = inp;
#ifdef DEBUG
count_long out_count = 0;
#endif
if (nomagic == 0) {
if ((putc(magic_header[0], outp) == EOF ||
putc(magic_header[1], outp) == EOF ||
putc((char)(maxbits | block_compress),
outp) == EOF) &&
ferror(outp)) {
ioerror();
}
}
offset = 0;
bytes_out = 3; /* includes 3-byte header mojo */
clear_flg = 0;
ratio = 0;
in_count = 1;
inchi = 0;
inclo = 1;
checkpoint = CHECK_GAP;
maxcode = MAXCODE(n_bits = INIT_BITS);
free_ent = ((block_compress) ? FIRST : 256);
if ((ent = getc(fin)) == EOF && ferror(fin)) {
ioerror();
}
hshift = 0;
for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L)
hshift++;
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
maxbits_reg = maxbits;
cl_hash((count_int) hsize_reg); /* clear hash table */
while ((c = getc(fin)) != EOF) {
if (++inclo == 0)
inchi++;
fcode = (long)(((long)c << maxbits_reg) + ent);
i = ((c << hshift) ^ ent); /* xor hashing */
if ((unsigned int)i >= hsize_reg)
i = adjusti(i, hsize_reg);
if (htabof(i) == fcode) {
ent = codetabof(i);
continue;
} else if ((long)htabof(i) < 0) {
/* empty slot */
goto nomatch;
}
/* secondary hash (after G. Knott) */
disp = hsize_reg - i;
if (i == 0) {
disp = 1;
}
probecnt = 0;
probe:
if (++probecnt > hsize_reg)
cinterr(hshift);
if ((i -= disp) < 0) {
while (i < 0)
i += hsize_reg;
}
if (htabof(i) == fcode) {
ent = codetabof(i);
continue;
}
if ((long)htabof(i) > 0) {
goto probe;
}
nomatch:
output((code_int) ent);
#ifdef DEBUG
out_count++;
#endif
ent = c;
if (free_ent < maxmaxcode) {
codetabof(i) = free_ent++;
/* code -> hashtable */
htabof(i) = fcode;
} else {
in_count = ((long long)inchi<<32|inclo);
if ((count_long)in_count >=
(count_long)checkpoint && block_compress) {
cl_block(in_count);
}
}
}
in_count = ((long long)inchi<<32|inclo);
if (ferror(fin) != 0) {
ioerror();
}
/*
* Put out the final code.
*/
output((code_int)ent);
#ifdef DEBUG
out_count++;
#endif
output((code_int)-1);
/*
* Print out stats on stderr
*/
if (!quiet) {
#ifdef DEBUG
(void) fprintf(stderr,
"%lld chars in, %lld codes (%lld bytes) out, "
"compression factor: ",
(count_long)in_count, (count_long)out_count,
(count_long) bytes_out);
prratio(stderr, (count_long)in_count,
(count_long)bytes_out);
(void) fprintf(stderr, "\n");
(void) fprintf(stderr, "\tCompression as in compact: ");
prratio(stderr,
(count_long)in_count-(count_long)bytes_out,
(count_long)in_count);
(void) fprintf(stderr, "\n");
(void) fprintf(stderr,
"\tLargest code (of last block) was %d"
" (%d bits)\n",
free_ent - 1, n_bits);
#else /* !DEBUG */
(void) fprintf(stderr, gettext("Compression: "));
prratio(stderr,
(count_long)in_count-(count_long)bytes_out,
(count_long)in_count);
#endif /* DEBUG */
}
/* report if no savings */
if ((count_long)bytes_out > (count_long)in_count) {
didnt_shrink = 1;
}
}
/*
* **************************************************************
* TAG(output)
*
* Output the given code.
* Inputs:
* code: A n_bits-bit integer. If == -1, then EOF. This assumes
* that n_bits = < (long)wordsize - 1.
* Outputs:
* Outputs code to the file.
* Assumptions:
* Chars are 8 bits long.
* Algorithm:
* Maintain a BITS character long buffer(so that 8 codes will
* fit in it exactly). Use the VAX insv instruction to insert each
* code in turn. When the buffer fills up empty it and start over.
*/
static void
output(code_int code)
{
#ifdef DEBUG
static int col = 0;
#endif /* DEBUG */
int r_off = offset, bits = n_bits;
char *bp = buf;
#ifdef DEBUG
if (verbose)
(void) fprintf(stderr, "%5d%c", code,
(col += 6) >= 74 ? (col = 0, '\n') : ' ');
#endif /* DEBUG */
if (code >= 0) {
/*
* byte/bit numbering on the VAX is simulated
* by the following code
*/
/*
* Get to the first byte.
*/
bp += (r_off >> 3);
r_off &= 7;
/*
* Since code is always >= 8 bits, only need to mask the first
* hunk on the left.
*/
*bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
bp++;
bits -= (8 - r_off);
code >>= 8 - r_off;
/*
* Get any 8 bit parts in the middle (<=1 for up to 16
* bits).
*/
if (bits >= 8) {
*bp++ = code;
code >>= 8;
bits -= 8;
}
/* Last bits. */
if (bits)
*bp = code;
offset += n_bits;
if (offset == (n_bits << 3)) {
bp = buf;
bits = n_bits;
bytes_out += bits;
do {
if (putc(*bp, outp) == EOF &&
ferror(outp)) {
ioerror();
}
bp++;
} while (--bits);
offset = 0;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if (free_ent > maxcode || (clear_flg > 0)) {
/*
* Write the whole buffer, because the input
* side won't discover the size increase until
* after it has read it.
*/
if (offset > 0) {
if (fwrite(buf, 1, n_bits, outp) != n_bits) {
longjmp(env, 3);
}
bytes_out += n_bits;
}
offset = 0;
if (clear_flg) {
maxcode = MAXCODE(n_bits = INIT_BITS);
clear_flg = 0;
} else {
n_bits++;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
#ifdef DEBUG
if (debug) {
(void) fprintf(stderr,
"\nChange to %d bits\n", n_bits);
col = 0;
}
#endif /* DEBUG */
}
} else {
/*
* At EOF, write the rest of the buffer.
*/
if (offset > 0) {
if (fwrite(buf, 1, (offset + 7) / 8, outp) == 0 &&
ferror(outp)) {
ioerror();
}
bytes_out += (offset + 7) / 8;
}
offset = 0;
(void) fflush(outp);
#ifdef DEBUG
if (verbose)
(void) fprintf(stderr, "\n");
#endif /* DEBUG */
if (ferror(outp))
ioerror();
}
}
/*
* Decompress inp to outp. This routine adapts to the codes in the
* file building the "string" table on-the-fly; requiring no table to
* be stored in the compressed file. The tables used herein are shared
* with those of the compress() routine. See the definitions above.
*/
static void
decompress()
{
char_type *stackp, *stack_lim;
int finchar;
code_int code, oldcode, incode;
FILE *fout = outp;
/*
* As above, initialize the first 256 entries in the table.
*/
maxcode = MAXCODE(n_bits = INIT_BITS);
for (code = 255; code >= 0; code--) {
tab_prefixof(code) = 0;
tab_suffixof(code) = (char_type)code;
}
free_ent = ((block_compress) ? FIRST : 256);
finchar = oldcode = getcode();
if (oldcode == -1) /* EOF already? */
return; /* Get out of here */
/* first code must be 8 bits = char */
if (putc((char)finchar, outp) == EOF && ferror(outp)) {
/* Crash if can't write */
ioerror();
}
stackp = de_stack;
stack_lim = stack_max;
while ((code = getcode()) > -1) {
if ((code == CLEAR) && block_compress) {
for (code = 255; code >= 0; code--)
tab_prefixof(code) = 0;
clear_flg = 1;
free_ent = FIRST - 1;
if ((code = getcode()) == -1) /* O, untimely death! */
break;
}
incode = code;
/*
* Special case for KwKwK string.
*/
if (code >= free_ent) {
if (stackp < stack_lim) {
*stackp++ = (char_type) finchar;
code = oldcode;
} else {
/* badness */
longjmp(env, 2);
}
}
/*
* Generate output characters in reverse order
*/
while (code >= 256) {
if (stackp < stack_lim) {
*stackp++ = tab_suffixof(code);
code = tab_prefixof(code);
} else {
/* badness */
longjmp(env, 2);
}
}
*stackp++ = finchar = tab_suffixof(code);
/*
* And put them out in forward order
*/
do {
stackp--;
(void) putc(*stackp, fout);
} while (stackp > de_stack);
if (ferror(fout))
ioerror();
/*
* Generate the new entry.
*/
if ((code = free_ent) < maxmaxcode) {
tab_prefixof(code) = (unsigned short) oldcode;
tab_suffixof(code) = (char_type) finchar;
free_ent = code+1;
}
/*
* Remember previous code.
*/
oldcode = incode;
}
(void) fflush(outp);
if (ferror(outp))
ioerror();
}
/*
* **************************************************************
* TAG( getcode )
*
* Read one code from the standard input. If EOF, return -1.
* Inputs:
* inp
* Outputs:
* code or -1 is returned.
*/
code_int
getcode() {
code_int code;
static int offset = 0, size = 0;
static char_type buf[BITS];
int r_off, bits;
char_type *bp = buf;
if (clear_flg > 0 || offset >= size || free_ent > maxcode) {
/*
* If the next entry will be too big for the current code
* size, then we must increase the size. This implies reading
* a new buffer full, too.
*/
if (free_ent > maxcode) {
n_bits++;
if (n_bits == maxbits)
/* won't get any bigger now */
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
if (clear_flg > 0) {
maxcode = MAXCODE(n_bits = INIT_BITS);
clear_flg = 0;
}
size = fread(buf, 1, n_bits, inp);
if (size <= 0) {
if (feof(inp)) {
/* end of file */
return (-1);
} else if (ferror(inp)) {
ioerror();
}
}
offset = 0;
/* Round size down to integral number of codes */
size = (size << 3) - (n_bits - 1);
}
r_off = offset;
bits = n_bits;
/*
* Get to the first byte.
*/
bp += (r_off >> 3);
r_off &= 7;
/* Get first part (low order bits) */
code = (*bp++ >> r_off);
bits -= (8 - r_off);
r_off = 8 - r_off; /* now, offset into code word */
/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
if (bits >= 8) {
code |= *bp++ << r_off;
r_off += 8;
bits -= 8;
}
/* high order bits. */
code |= (*bp & rmask[bits]) << r_off;
offset += n_bits;
return (code);
}
#ifdef DEBUG
static void
printcodes()
{
/*
* Just print out codes from input file. For debugging.
*/
code_int code;
int col = 0, bits;
bits = n_bits = INIT_BITS;
maxcode = MAXCODE(n_bits);
free_ent = ((block_compress) ? FIRST : 256);
while ((code = getcode()) >= 0) {
if ((code == CLEAR) && block_compress) {
free_ent = FIRST - 1;
clear_flg = 1;
} else if (free_ent < maxmaxcode)
free_ent++;
if (bits != n_bits) {
(void) fprintf(stderr, "\nChange to %d bits\n", n_bits);
bits = n_bits;
col = 0;
}
(void) fprintf(stderr, "%5d%c",
code, (col += 6) >= 74 ? (col = 0, '\n') : ' ');
}
(void) putc('\n', stderr);
}
#endif /* DEBUG */
#ifdef DEBUG
static void
dump_tab() /* dump string table */
{
int i, first;
int ent;
int stack_top = STACK_SIZE;
int c;
if (do_decomp == 0) { /* compressing */
int flag = 1;
for (i = 0; i < hsize; i++) { /* build sort pointers */
if ((long)htabof(i) >= 0) {
sorttab[codetabof(i)] = i;
}
}
first = block_compress ? FIRST : 256;
for (i = first; i < free_ent; i++) {
(void) fprintf(stderr, "%5d: \"", i);
de_stack[--stack_top] = '\n';
de_stack[--stack_top] = '"';
stack_top =
in_stack((htabof(sorttab[i]) >> maxbits) & 0xff,
stack_top);
for (ent = htabof(sorttab[i]) & ((1 << maxbits) -1);
ent > 256;
ent = htabof(sorttab[ent]) & ((1<<maxbits)-1)) {
stack_top = in_stack(
htabof(sorttab[ent]) >> maxbits,
stack_top);
}
stack_top = in_stack(ent, stack_top);
(void) fwrite(&de_stack[stack_top], 1,
STACK_SIZE - stack_top, stderr);
stack_top = STACK_SIZE;
}
} else if (!debug) { /* decompressing */
for (i = 0; i < free_ent; i++) {
ent = i;
c = tab_suffixof(ent);
if (isascii(c) && isprint(c))
(void) fprintf(stderr, "%5d: %5d/'%c' \"",
ent, tab_prefixof(ent), c);
else
(void) fprintf(stderr, "%5d: %5d/\\%03o \"",
ent, tab_prefixof(ent), c);
de_stack[--stack_top] = '\n';
de_stack[--stack_top] = '"';
for (; ent != NULL;
ent = (ent >= FIRST ? tab_prefixof(ent) :
NULL)) {
stack_top = in_stack(tab_suffixof(ent),
stack_top);
}
(void) fwrite(&de_stack[stack_top], 1,
STACK_SIZE - stack_top, stderr);
stack_top = STACK_SIZE;
}
}
}
#endif /* DEBUG */
#ifdef DEBUG
static int
in_stack(int c, int stack_top)
{
if ((isascii(c) && isprint(c) && c != '\\') || c == ' ') {
de_stack[--stack_top] = c;
} else {
switch (c) {
case '\n': de_stack[--stack_top] = 'n'; break;
case '\t': de_stack[--stack_top] = 't'; break;
case '\b': de_stack[--stack_top] = 'b'; break;
case '\f': de_stack[--stack_top] = 'f'; break;
case '\r': de_stack[--stack_top] = 'r'; break;
case '\\': de_stack[--stack_top] = '\\'; break;
default:
de_stack[--stack_top] = '0' + c % 8;
de_stack[--stack_top] = '0' + (c / 8) % 8;
de_stack[--stack_top] = '0' + c / 64;
break;
}
de_stack[--stack_top] = '\\';
}
return (stack_top);
}
#endif /* DEBUG */
static void
ioerror()
{
longjmp(env, 1);
}
static void
copystat(char *ifname, struct stat *ifstat, char *ofname)
{
mode_t mode;
struct utimbuf timep;
acl_t *aclp = NULL;
int error;
if (fclose(outp)) {
perror(ofname);
if (!quiet) {
(void) fprintf(stderr, gettext(" -- file unchanged"));
newline_needed = 1;
}
perm_stat = 1;
} else if (ifstat == NULL) { /* Get stat on input file */
perror(ifname);
return;
} else if ((ifstat->st_mode &
S_IFMT /* 0170000 */) != S_IFREG /* 0100000 */) {
if (quiet) {
(void) fprintf(stderr, "%s: ", ifname);
}
(void) fprintf(stderr, gettext(
" -- not a regular file: unchanged"));
newline_needed = 1;
perm_stat = 1;
} else if (ifstat->st_nlink > 1) {
if (quiet) {
(void) fprintf(stderr, "%s: ", ifname);
}
(void) fprintf(stderr, gettext(
" -- has %d other links: unchanged"),
(uint_t)ifstat->st_nlink - 1);
newline_needed = 1;
perm_stat = 1;
} else if (didnt_shrink && !force) {
/* No compression: remove file.Z */
if (!quiet) {
(void) fprintf(stderr, gettext(
" -- file unchanged"));
newline_needed = 1;
}
} else if ((pathconf(ifname, _PC_XATTR_EXISTS) == 1) &&
(mv_xattrs(ifname, ofname, 0) < 0)) {
(void) fprintf(stderr, gettext(
"%s: -- cannot preserve extended attributes, "
"file unchanged"), ifname);
newline_needed = 1;
/* Move attributes back ... */
(void) mv_xattrs(ofname, ifname, 1);
perm_stat = 1;
} else { /* ***** Successful Compression ***** */
mode = ifstat->st_mode & 07777;
if (chmod(ofname, mode)) /* Copy modes */
perror(ofname);
error = acl_get(ifname, ACL_NO_TRIVIAL, &aclp);
if (error != 0) {
(void) fprintf(stderr, gettext(
"%s: failed to retrieve acl : %s\n"),
ifname, acl_strerror(error));
perm_stat = 1;
}
if (aclp && (acl_set(ofname, aclp) < 0)) {
(void) fprintf(stderr, gettext("%s: failed to set acl "
"entries\n"), ofname);
perm_stat = 1;
}
if (aclp) {
acl_free(aclp);
aclp = NULL;
}
/* Copy ownership */
(void) chown(ofname, ifstat->st_uid, ifstat->st_gid);
timep.actime = ifstat->st_atime;
timep.modtime = ifstat->st_mtime;
/* Update last accessed and modified times */
(void) utime(ofname, &timep);
if (unlink(ifname)) /* Remove input file */
perror(ifname);
if (!quiet) {
(void) fprintf(stderr, gettext(
" -- replaced with %s"), ofname);
newline_needed = 1;
}
return; /* Successful return */
}
/* Unsuccessful return -- one of the tests failed */
if (ofname[0] != '\0') {
if (unlink(ofname)) {
perror(ofname);
}
ofname[0] = '\0';
}
}
static void
onintr()
{
if (!precious && !use_stdout && ofname[0] != '\0')
(void) unlink(ofname);
exit(1);
}
static void
oops() /* wild pointer -- assume bad input */
{
if (do_decomp) {
(void) fprintf(stderr, gettext("uncompress: corrupt input\n"));
}
if (!use_stdout && ofname[0] != '\0') {
(void) unlink(ofname);
}
exit(1);
}
static void
cl_block(count_long in_count) /* table clear for block compress */
{
count_long rat;
checkpoint = (count_long)in_count + (count_long)CHECK_GAP;
#ifdef DEBUG
if (debug) {
(void) fprintf(stderr, "count: %lld, ratio: ",
(count_long)in_count);
prratio(stderr, (count_long)in_count, (count_long)bytes_out);
(void) fprintf(stderr, "\n");
}
#endif /* DEBUG */
/* shift will overflow */
if ((count_long)in_count > 0x007fffffffffffffLL) {
rat = (count_long)bytes_out >> 8;
if (rat == 0) { /* Don't divide by zero */
rat = 0x7fffffffffffffffLL;
} else {
rat = (count_long)in_count / (count_long)rat;
}
} else {
/* 8 fractional bits */
rat = ((count_long)in_count << 8) /(count_long)bytes_out;
}
if (rat > ratio) {
ratio = rat;
} else {
ratio = 0;
#ifdef DEBUG
if (verbose)
dump_tab(); /* dump string table */
#endif
cl_hash((count_int) hsize);
free_ent = FIRST;
clear_flg = 1;
output((code_int) CLEAR);
#ifdef DEBUG
if (debug)
(void) fprintf(stderr, "clear\n");
#endif /* DEBUG */
}
}
static void
cl_hash(count_int hsize) /* reset code table */
{
count_int *htab_p = htab+hsize;
long i;
long m1 = -1;
i = hsize - 16;
do { /* might use Sys V memset(3) here */
*(htab_p-16) = m1;
*(htab_p-15) = m1;
*(htab_p-14) = m1;
*(htab_p-13) = m1;
*(htab_p-12) = m1;
*(htab_p-11) = m1;
*(htab_p-10) = m1;
*(htab_p-9) = m1;
*(htab_p-8) = m1;
*(htab_p-7) = m1;
*(htab_p-6) = m1;
*(htab_p-5) = m1;
*(htab_p-4) = m1;
*(htab_p-3) = m1;
*(htab_p-2) = m1;
*(htab_p-1) = m1;
htab_p -= 16;
} while ((i -= 16) >= 0);
for (i += 16; i > 0; i--)
*--htab_p = m1;
}
static void
prratio(FILE *stream, count_long num, count_long den)
{
int q; /* store percentage */
q = (int)(10000LL * (count_long)num / (count_long)den);
if (q < 0) {
(void) putc('-', stream);
q = -q;
}
(void) fprintf(stream, "%d%s%02d%%", q / 100,
localeconv()->decimal_point, q % 100);
}
static void
version()
{
(void) fprintf(stderr, "%s, Berkeley 5.9 5/11/86\n", rcs_ident);
(void) fprintf(stderr, "Options: ");
#ifdef DEBUG
(void) fprintf(stderr, "DEBUG, ");
#endif
(void) fprintf(stderr, "BITS = %d\n", BITS);
}
static void
Usage()
{
#ifdef DEBUG
(void) fprintf(stderr,
"Usage: compress [-dDVfc] [-b maxbits] [file ...]\n");
#else
if (strcmp(progname, "compress") == 0) {
(void) fprintf(stderr,
gettext(
"Usage: compress [-fv] [-b maxbits] [file ...]\n"\
" compress [-cfv] [-b maxbits] [file]\n"));
} else if (strcmp(progname, "uncompress") == 0)
(void) fprintf(stderr, gettext(
"Usage: uncompress [-cfv] [file ...]\n"));
else if (strcmp(progname, "zcat") == 0)
(void) fprintf(stderr, gettext("Usage: zcat [file ...]\n"));
#endif /* DEBUG */
}
static char *
local_basename(char *path)
{
char *p;
char *ret = (char *)path;
while ((p = (char *)strpbrk(ret, "/")) != NULL)
ret = p + 1;
return (ret);
}
static int
addDotZ(char *fn, size_t fnsize)
{
char *fn_dup;
char *dir;
long int max_name;
long int max_path;
fn_dup = strdup(fn);
dir = dirname(fn_dup);
max_name = pathconf(dir, _PC_NAME_MAX);
max_path = pathconf(dir, _PC_PATH_MAX);
free(fn_dup);
/* Check for component length too long */
if ((strlen(local_basename(fn)) + 2) > (size_t)max_name) {
(void) fprintf(stderr,
gettext("%s: filename too long to tack on .Z:"
" %s\n"), progname, fn);
return (-1);
}
/* Check for path length too long */
if ((strlen(fn) + 2) > (size_t)max_path - 1) {
(void) fprintf(stderr,
gettext("%s: Pathname too long to tack on .Z:"
" %s\n"), progname, fn);
return (-1);
}
if (strlcat(fn, ".Z", fnsize) >= fnsize) {
(void) fprintf(stderr,
gettext("%s: Buffer overflow adding .Z to %s\n"),
progname, fn);
return (-1);
}
return (0);
}
/*
* mv_xattrs - move (via renameat) all of the extended attributes
* associated with the file infile to the file outfile.
* This function returns 0 on success and -1 on error.
*/
static int
mv_xattrs(char *infile, char *outfile, int silent)
{
int indfd, outdfd, tmpfd;
DIR *dirp = NULL;
struct dirent *dp = NULL;
int error = 0;
char *etext;
indfd = outdfd = tmpfd = -1;
if ((indfd = attropen(infile, ".", O_RDONLY)) == -1) {
etext = gettext("cannot open source");
error = -1;
goto out;
}
if ((outdfd = attropen(outfile, ".", O_RDONLY)) == -1) {
etext = gettext("cannot open target");
error = -1;
goto out;
}
if ((tmpfd = dup(indfd)) == -1) {
etext = gettext("cannot dup descriptor");
error = -1;
goto out;
}
if ((dirp = fdopendir(tmpfd)) == NULL) {
etext = gettext("cannot access source");
error = -1;
goto out;
}
while (dp = readdir(dirp)) {
if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') ||
(dp->d_name[0] == '.' && dp->d_name[1] == '.' &&
dp->d_name[2] == '\0'))
continue;
if ((renameat(indfd, dp->d_name, outdfd, dp->d_name)) == -1) {
etext = dp->d_name;
error = -1;
goto out;
}
}
out:
if (error == -1 && silent == 0) {
if (quiet) {
(void) fprintf(stderr, "%s: ", infile);
} else {
(void) fprintf(stderr, ", ");
}
(void) fprintf(stderr, gettext("extended attribute error: "));
perror(etext);
}
if (dirp)
(void) closedir(dirp);
if (indfd != -1)
(void) close(indfd);
if (outdfd != -1)
(void) close(outdfd);
return (error);
}