1277 lines · 32.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * compress - File compression ala IEEE Computer, June 1984. * * 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: *	-b:	limit the max number of bits/code. *	-c:	write output on stdout, don't remove original. *	-d:	decompress instead. *	-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.  * Authors:	Spencer W. Thomas	(decvax!harpo!utah-cs!utah-gr!thomas) *		Jim McKie		(decvax!mcvax!jim) *		Steve Davies		(decvax!vax135!petsd!peora!srd) *		Ken Turkowski		(decvax!decwrl!turtlevax!ken) *		James A. Woods		(decvax!ihnp4!ames!jaw) *		Joe Orost		(decvax!vax135!petsd!joe) */#define _PLAN9_SOURCE#define _BSD_EXTENSION#define _POSIX_SOURCE #include <u.h>#include <stdio.h>#include <ctype.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <utime.h>#include <sys/types.h>#include <sys/stat.h> #define	min(a,b)	((a>b) ? b : a) #define BITS	16#define HSIZE	69001		/* 95% occupancy */ /* * a code_int must be able to hold 2**BITS values of type int, and also -1 */typedef long	code_int;typedef long	count_int; static char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $"; uchar magic_header[] = { 0x1F, 0x9D };	/* 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 */ #define ARGVAL() (*++(*argv) || (--argc && *++argv)) int n_bits;				/* number of bits/code */int maxbits = BITS;			/* user settable max # bits/code */code_int maxcode;			/* maximum code, given n_bits */code_int maxmaxcode = 1 << BITS;	/* should NEVER generate this code */ #define MAXCODE(n_bits)	((1 << (n_bits)) - 1) count_int htab[HSIZE];ushort codetab[HSIZE]; #define htabof(i)	htab[i]#define codetabof(i)	codetab[i] code_int hsize = HSIZE;			/* for dynamic table sizing */count_int fsize; /* * 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)	((uchar *)(htab))[i]#define de_stack		((uchar *)&tab_suffixof(1<<BITS)) code_int free_ent = 0;			/* first unused entry */int exit_stat = 0; void	cl_block(void);void	cl_hash(count_int);void	compress(void);void	copystat(char *, char *);void	decompress(void);int	foreground(void);code_int getcode(void);void	onintr(int);void	oops(int);void	output(code_int);void	prratio(FILE *, long, long);void	version(void);void	writeerr(void); voidUsage(void){#ifdef DEBUG	fprintf(stderr,"Usage: compress [-cdfDV] [-b maxbits] [file ...]\n");#else	fprintf(stderr,"Usage: compress [-cdfvV] [-b maxbits] [file ...]\n");#endif /* DEBUG */} int debug = 0;int nomagic = 0;	/* Use a 3-byte magic number header, unless old file */int zcat_flg = 0;	/* Write output on stdout, suppress messages */int quiet = 1;		/* don't tell me about compression */ /* * block compression parameters -- after all codes are used up, * and compression rate changes, start over. */int block_compress = BLOCK_MASK;int clear_flg = 0;long ratio = 0;#define CHECK_GAP 10000	/* ratio check interval */count_int 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 */ int force = 0;char ofname [100];#ifdef DEBUGint verbose = 0;#endif /* DEBUG */void (*bgnd_flag)(int); int do_decomp = 0; main(argc, argv)int argc;char **argv;{	int overwrite = 0;	/* Do not overwrite unless given -f flag */	char tempname[512];	char **filelist, **fileptr;	char *cp;	struct stat statbuf; 	if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) {		signal(SIGINT, onintr);		signal(SIGSEGV, oops);	} 	filelist = fileptr = (char **)(malloc(argc * sizeof(*argv)));	*filelist = NULL; 	if((cp = strrchr(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_flg = 1;	} 	/*	 * Argument Processing	 * All flags are optional.	 * -C	generate output compatible with compress 2.0.	 * -D	debug	 * -V	print Version; debug verbose	 * -b maxbits	maxbits.  If -b is specified, then maxbits MUST be	 *		given also.	 * -c	cat all output to stdout	 * -d	do_decomp	 * -f	force overwrite of output file	 * -n	no header: useful to uncompress old files	 * -v	unquiet	 * if a string is left, must be an input filename.	 */	for (argc--, argv++; argc > 0; argc--, argv++) {	if (**argv == '-') {	/* A flag argument */		while (*++(*argv)) {	/* Process all flags in this arg */		switch (**argv) {		case 'C':			block_compress = 0;			break;#ifdef DEBUG		case 'D':			debug = 1;			break;		case 'V':			verbose = 1;			version();			break;#else		case 'V':			version();			break;#endif		case 'b':			if (!ARGVAL()) {				fprintf(stderr, "Missing maxbits\n");				Usage();				exit(1);			}			maxbits = atoi(*argv);			goto nextarg;		case 'c':			zcat_flg = 1;			break;		case 'd':			do_decomp = 1;			break;		case 'f':		case 'F':			overwrite = 1;			force = 1;			break;		case 'n':			nomagic = 1;			break;		case 'q':			quiet = 1;			break;		case 'v':			quiet = 0;			break;		default:			fprintf(stderr, "Unknown flag: '%c'; ", **argv);			Usage();			exit(1);		}		}	} else {			/* Input file name */		*fileptr++ = *argv;	/* Build input file list */		*fileptr = NULL;		/* process nextarg; */	}nextarg:		continue;	} 	if(maxbits < INIT_BITS) maxbits = INIT_BITS;	if (maxbits > BITS) maxbits = BITS;	maxmaxcode = 1 << maxbits; 	if (*filelist != NULL) {	for (fileptr = filelist; *fileptr; fileptr++) {		exit_stat = 0;		if (do_decomp != 0) {			/* DECOMPRESSION */		/* Check for .Z suffix */		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) {			/* No .Z: tack one on */			strcpy(tempname, *fileptr);			strcat(tempname, ".Z");			*fileptr = tempname;		}		/* Open input file */		if ((freopen(*fileptr, "r", stdin)) == NULL) {			perror(*fileptr);			continue;		}		/* Check the magic number */		if (nomagic == 0) {			if ((getchar() != (magic_header[0] & 0xFF))			|| (getchar() != (magic_header[1] & 0xFF))) {				fprintf(stderr, "%s: not in compressed format\n",					*fileptr);				continue;			}			maxbits = getchar();	/* set -b from file */			block_compress = maxbits & BLOCK_MASK;			maxbits &= BIT_MASK;			maxmaxcode = 1 << maxbits;			if(maxbits > BITS) {				fprintf(stderr,		"%s: compressed with %d bits, can only handle %d bits\n",					*fileptr, maxbits, BITS);				continue;			}		}		/* Generate output filename */		strcpy(ofname, *fileptr);		ofname[strlen(*fileptr) - 2] = '\0';  /* Strip off .Z */		} else {				/* COMPRESSION */		if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) {			fprintf(stderr,				"%s: already has .Z suffix -- no change\n",				*fileptr);			continue;		}		/* Open input file */		if ((freopen(*fileptr, "r", stdin)) == NULL) {			perror(*fileptr);			continue;		}		(void) stat(*fileptr, &statbuf);		fsize = (long) 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); 		/* Generate output filename */		strcpy(ofname, *fileptr);#ifndef BSD4_2		if ((cp=strrchr(ofname,'/')) != NULL)			cp++;		else			cp = ofname;		/*		 *** changed 12 to 25;  should be NAMELEN-3, but I don't want		 * to fight the headers.	ehg 5 Nov 92 **		 */		if (strlen(cp) > 25) {			fprintf(stderr, "%s: filename too long to tack on .Z\n",				cp);			continue;		}#endif		strcat(ofname, ".Z");		}		/* Check for overwrite of existing file */		if (overwrite == 0 && zcat_flg == 0 &&		   stat(ofname, &statbuf) == 0) {			char response[2]; 			response[0] = 'n';			fprintf(stderr, "%s already exists;", ofname);			if (foreground()) {				fprintf(stderr,				    " do you wish to overwrite %s (y or n)? ",					ofname);				fflush(stderr);				(void) read(2, response, 2);				while (response[1] != '\n')					if (read(2, response+1, 1) < 0) {						/* Ack! */						perror("stderr");						break;					}			}			if (response[0] != 'y') {				fprintf(stderr, "\tnot overwritten\n");				continue;			}		}		if(zcat_flg == 0) {		/* Open output file */			if (freopen(ofname, "w", stdout) == NULL) {				perror(ofname);				continue;			}			if(!quiet)				fprintf(stderr, "%s: ", *fileptr);		} 		/* Actually do the compression/decompression */		if (do_decomp == 0)			compress();#ifndef DEBUG		else			decompress();#else		else if (debug == 0)			decompress();		else			printcodes();		if (verbose)			dump_tab();#endif						/* DEBUG */		if(zcat_flg == 0) {			copystat(*fileptr, ofname);	/* Copy stats */			if (exit_stat == 1 || !quiet)				putc('\n', stderr);		}	}	} else {		/* Standard input */	if (do_decomp == 0) {		compress();#ifdef DEBUG		if(verbose)			dump_tab();#endif		if(!quiet)			putc('\n', stderr);	} else {		/* Check the magic number */		if (nomagic == 0) {		if ((getchar()!=(magic_header[0] & 0xFF))		 || (getchar()!=(magic_header[1] & 0xFF))) {			fprintf(stderr, "stdin: not in compressed format\n");			exit(1);		}		maxbits = getchar();	/* set -b from file */		block_compress = maxbits & BLOCK_MASK;		maxbits &= BIT_MASK;		maxmaxcode = 1 << maxbits;		fsize = 100000;		/* assume stdin large for USERMEM */		if(maxbits > BITS) {			fprintf(stderr,			"stdin: compressed with %d bits, can only handle %d bits\n",			maxbits, BITS);			exit(1);		}		}#ifndef DEBUG		decompress();#else		if (debug == 0)			decompress();		else			printcodes();		if (verbose)			dump_tab();#endif						/* DEBUG */	}	}	exit(exit_stat);	return 0;} static int offset;long in_count = 1;		/* length of input */long bytes_out;			/* length of compressed output */long out_count = 0;		/* # of codes output (for debugging) */ /* * compress stdin to stdout * * 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. */voidcompress(void){	code_int ent, hsize_reg;	code_int i;	int c, disp, hshift;	long fcode; 	if (nomagic == 0) {

Lines 1–500 of 1277.