/* * Copyright 2005 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< 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 #include #include #include #include #include #include #include /* XCU4 */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Multi-byte handling for 'y' or 'n' */ static char *yesstr; /* string contains int'l for "yes" */ static char *nostr; /* string contains int'l for "yes" */ static int ynsize = 0; /* # of (multi)bytes for "y" */ static char *yesorno; /* int'l input for 'y' */ 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< 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) { yesorno[ynsize] = (char)NULL; (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); (void) strncpy(yesorno, line, ynsize); if (!((strncmp(yesstr, yesorno, ynsize) == 0) || (yesorno[0] == 'y') || (yesorno[0] == 'Y'))) { (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, 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; 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); /* 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); }