$! ................... Cut between dotted lines and save. ................... $!........................................................................... $! VAX/VMS archive file created by VMS_SHARE V06.10 7-FEB-1989. $! $! VMS_SHARE was written by James Gray (Gray:OSBUSouth@Xerox.COM) from $! VMS_SHAR by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au). $! $! To unpack, simply save, concatinate all parts into one file and $! execute (@) that file. $! $! This archive was created by user PHILIPPE $! on 19-FEB-1990 10:27:45.95. $! $! It contains the following 2 files: $! COMPRESS.README $! COMPRESS.C $! $!============================================================================ $ SET SYMBOL/SCOPE=( NOLOCAL, NOGLOBAL ) $ VERSION = F$GETSYI( "VERSION" ) $ IF VERSION .GES "V4.4" THEN GOTO VERSION_OK $ WRITE SYS$OUTPUT "You are running VMS ''VERSION'; ", - "VMS_SHARE V06.10 7-FEB-1989 requires VMS V4.4 or higher." $ EXIT 44 ! SS$_ABORT $VERSION_OK: $ GOTO START $! $UNPACK_FILE: $ WRITE SYS$OUTPUT "Creating ''FILE_IS'" $ DEFINE/USER_MODE SYS$OUTPUT NL: $ EDIT/TPU/COMMAND=SYS$INPUT/NODISPLAY/OUTPUT='FILE_IS'/NOSECTION - VMS_SHARE_DUMMY.DUMMY b_part := CREATE_BUFFER( "{Part}", GET_INFO( COMMAND_LINE, "file_name" ) ) ; s_file_spec := GET_INFO( COMMAND_LINE, "output_file" ); SET( OUTPUT_FILE , b_part, s_file_spec ); b_errors := CREATE_BUFFER( "{Errors}" ); i_errors := 0; pat_beg_1 := ANCHOR & "-+-+-+ Beginning"; pat_beg_2 := LINE_BEGIN & "+-+-+-+ Beginning"; pat_end := ANCHOR & "+-+-+-+-+ End"; POSITION ( BEGINNING_OF( b_part ) ); LOOP EXITIF SEARCH( SPAN( ' ' )@r_trail & LINE_END, FORWARD) = 0; POSITION( r_trail ); ERASE( r_trail ); ENDLOOP ; POSITION( BEGINNING_OF( b_part ) ); i_append_line := 0; LOOP EXITIF MARK ( NONE ) = END_OF( b_part ); s_x := ERASE_CHARACTER( 1 ) ; IF s_x = '+' THEN r_skip := SEARCH( pat_beg_1, FORWARD, EXACT ); IF r_skip <> 0 THEN s_x := ''; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ERASE_LINE; ENDIF ; ENDIF; IF s_x = '-' THEN r_skip := SEARCH( pat_end, FORWARD, EXACT ) ; IF r_skip <> 0 THEN s_x := ''; MOVE_HORIZONTAL( -CURRENT_OFFSET ); m_skip := MARK( NONE ); r_skip := SEARCH( pat_beg_2, FORWARD, EXACT ); IF r_skip <> 0 THEN POSITION( END_OF( r_skip ) ); MOVE_HORIZONTAL( -CURRENT_OFFSET ) ; MOVE_VERTICAL( 1 ); MOVE_HORIZONTAL( -1 ); ELSE POSITION( END_OF( b_part ) ); ENDIF; ERASE( CREATE_RANGE( m_skip, MARK( NONE ), NONE ) ); ENDIF; ENDIF ; IF s_x = 'V' THEN s_x := ''; IF i_append_line <> 0 THEN APPEND_LINE ; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF; i_append_line := 1 ; MOVE_VERTICAL( 1 ); ENDIF; IF s_x = 'X' THEN s_x := ''; IF i_append_line <> 0 THEN APPEND_LINE; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF ; i_append_line := 0; MOVE_VERTICAL( 1 ); ENDIF; IF s_x <> '' THEN i_errors := i_errors + 1; s_text := CURRENT_LINE; POSITION( b_errors ); COPY_TEXT ( "The following line could not be unpacked properly:" ); SPLIT_LINE ; COPY_TEXT( s_x ); COPY_TEXT( s_text ); POSITION( b_part ); MOVE_VERTICAL ( 1 ); ENDIF; ENDLOOP; POSITION( BEGINNING_OF( b_part ) ); LOOP r_x := SEARCH ( "`", FORWARD, EXACT ); EXITIF r_x = 0; POSITION( r_x ); ERASE_CHARACTER( 1 ); COPY_TEXT( ASCII( INT( ERASE_CHARACTER( 3 ) ) ) ); ENDLOOP ; IF i_errors = 0 THEN SET( NO_WRITE, b_errors, ON ); ELSE POSITION ( BEGINNING_OF( b_errors ) ); COPY_TEXT( FAO ( "The following !UL errors were detected while unpacking !AS", i_errors , s_file_spec ) ); SPLIT_LINE; SET( OUTPUT_FILE, b_errors, "SYS$COMMAND" ) ; ENDIF; EXIT; $ DELETE VMS_SHARE_DUMMY.DUMMY;* $ CHECKSUM 'FILE_IS $ WRITE SYS$OUTPUT " CHECKSUM ", - F$ELEMENT( CHECKSUM_IS .EQ. CHECKSUM$CHECKSUM, ",", "failed!!,passed." ) $ RETURN $! $START: $ FILE_IS = "COMPRESS.README" $ CHECKSUM_IS = 991222136 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X X`009@(#)README 1.1 86/09/25 SMI; from UCB 5.3 85/09/17 X XCompress version 4.0 improvements over 3.0: X`009o compress() speedup (10-50%) by changing division hash to xor X`009o decompress() speedup (5-10%) X`009o Memory requirements reduced (3-30%) X`009o Stack requirements reduced to less than 4kb X`009o Removed 'Big+Fast' compress code (FBITS) because of compress speedup X `009o Portability mods for Z8000 and PC/XT (but not zeus 3.2) X`009o Default to 'quiet' mode X`009o Unification of 'force' flags X`009o Manual page overhaul X`009o Portability enhancement for M_XENIX X`009o Removed text on #else and #endif X`009o Added "-V" switch to print version and options X`009o Added #defines for SIGNED_COMPARE_SLOW X`009o Added Makefile and "usermem" program X`009o Removed all floating point computations X`009o New programs: [deleted] X XThe "usermem" script attempts to determine the maximum process size. Some Xediting of the script may be necessary (see the comments). [It should work Xfine on 4.3 bsd.] If you can't get it to work at all, just create file X"USERMEM" containing the maximum process size in decimal. X XThe following preprocessor symbols control the compilation of "compress.c": X X`009o USERMEM`009`009Maximum process memory on the system X`009o SACREDMEM`009`009Amount to reserve for other proceses X`009o SIGNED_COMPARE_SLOW`009Unsigned compare instructions are faster X`009o NO_UCHAR`009`009Don't use "unsigned char" types X`009o BITS`009`009`009Overrules default set by USERMEM-SACREDMEM X`009o vax`009`009`009Generate inline assembler X`009o interdata`009`009Defines SIGNED_COMPARE_SLOW X`009o M_XENIX`009`009Makes arrays < 65536 bytes each X`009o pdp11`009`009`009BITS=12, NO_UCHAR X`009o z8000`009`009`009BITS=12 X`009o pcxt`009`009`009BITS=12 X`009o BSD4_2`009`009Allow long filenames ( > 14 characters) & X`009`009`009`009Call setlinebuf(stderr) X XThe difference "usermem-sacredmem" determines the maximum BITS that can be Xspecified with the "-b" flag. X Xmemory: at least`009`009BITS X------ -- ----- ---- X 433,484`009`009`009 16 X 229,600`009`009`009 15 X 127,536`009`009`009 14 X 73,464`009`009`009 13 X 0`009`009`009 12 X XThe default is BITS=16. X XThe maximum bits can be overrulled by specifying "-DBITS=bits" at Xcompilation time. X VWARNING: files compressed on a large machine with more bits than allowed by`0 X32 Xa version of compress on a smaller machine cannot be decompressed! Use the V"-b12" flag to generate a file on a large machine that can be uncompressed`03 X2 Xon a 16-bit machine. X XThe output of compress 4.0 is fully compatible with that of compress 3.0. XIn other words, the output of compress 4.0 may be fed into uncompress 3.0 or Xthe output of compress 3.0 may be fed into uncompress 4.0. X XThe output of compress 4.0 not compatible with that of Xcompress 2.0. However, compress 4.0 still accepts the output of Xcompress 2.0. To generate output that is compatible with compress X2.0, use the undocumented "-C" flag. X X`009-from mod.sources, submitted by vax135!petsd!joe (Joe Orost), 8/1/85 X-------------------------------- X XEnclosed is compress version 3.0 with the following changes: X X1.`009"Block" compression is performed. After the BITS run out, the X`009compression ratio is checked every so often. If it is decreasing, X`009the table is cleared and a new set of substrings are generated. X X`009This makes the output of compress 3.0 not compatible with that of X`009compress 2.0. However, compress 3.0 still accepts the output of X`009compress 2.0. To generate output that is compatible with compress X`0092.0, use the undocumented "-C" flag. X X2.`009A quiet "-q" flag has been added for use by the news system. X X3.`009The character chaining has been deleted and the program now uses X`009hashing. This improves the speed of the program, especially X`009during decompression. Other speed improvements have been made, X`009such as using putc() instead of fwrite(). X X4.`009A large table is used on large machines when a relatively small X`009number of bits is specified. This saves much time when compressing X`009for a 16-bit machine on a 32-bit virtual machine. Note that the X`009speed improvement only occurs when the input file is > 30000 X`009characters, and the -b BITS is less than or equal to the cutoff X`009described below. X XMost of these changes were made by James A. Woods (ames!jaw). Thank you XJames! X XTo compile compress: X X`009cc -O -DUSERMEM=usermem -o compress compress.c X VWhere "usermem" is the amount of physical user memory available (in bytes). ` X032 XIf any physical memory is to be reserved for other processes, put in`032 X"-DSACREDMEM sacredmem", where "sacredmem" is the amount to be reserved. X XThe difference "usermem-sacredmem" determines the maximum BITS that can be Xspecified, and the cutoff bits where the large+fast table is used. X Xmemory: at least`009`009BITS`009`009cutoff X------ -- ----- ---- ------ X 4,718,592 `009`009`009 16`009`009 13 X 2,621,440 `009`009`009 16`009`009 12 X 1,572,864`009`009`009 16`009`009 11 X 1,048,576`009`009`009 16`009`009 10 X 631,808`009`009`009 16 -- X 329,728`009`009`009 15 -- X 178,176`009`009`009 14`009`009 -- X 99,328`009`009`009 13`009`009 -- X 0`009`009`009 12`009`009 -- X XThe default memory size is 750,000 which gives a maximum BITS=16 and no Xlarge+fast table. X XThe maximum bits can be overruled by specifying "-DBITS=bits" at Xcompilation time. X XIf your machine doesn't support unsigned characters, define "NO_UCHAR"`032 Xwhen compiling. X XIf your machine has "int" as 16-bits, define "SHORT_INT" when compiling. X VAfter compilation, move "compress" to a standard executable location, such`03 X2 Xas /usr/local. Then: X`009cd /usr/local X`009ln compress uncompress X`009ln compress zcat X XOn machines that have a fixed stack size (such as Perkin-Elmer), set the Xstack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). X XNext, install the manual (compress.l). X`009cp compress.l /usr/man/manl X`009cd /usr/man/manl X`009ln compress.l uncompress.l X`009ln compress.l zcat.l X X`009`009- or - X X`009cp compress.l /usr/man/man1/compress.1 X`009cd /usr/man/man1 X`009ln compress.1 uncompress.1 X`009ln compress.1 zcat.1 X X`009`009`009`009`009regards, X`009`009`009`009`009petsd!joe X XHere is a note from the net: X X>From hplabs!pesnta!amd!turtlevax!ken Sat Jan 5 03:35:20 1985 XPath: ames!hplabs!pesnta!amd!turtlevax!ken XFrom: ken@turtlevax.UUCP (Ken Turkowski) XNewsgroups: net.sources XSubject: Re: Compress release 3.0 : sample Makefile XOrganization: CADLINC, Inc. @ Menlo Park, CA X XIn the compress 3.0 source recently posted to mod.sources, there is a X#define variable which can be set for optimum performance on a machine Xwith a large amount of memory. A program (usermem) to calculate the Xuseable amount of physical user memory is enclosed, as well as a sample X4.2bsd Vax Makefile for compress. X XHere is the README file from the previous version of compress (2.0): X X>Enclosed is compress.c version 2.0 with the following bugs fixed: X> X>1.`009The packed files produced by compress are different on different X>`009machines and dependent on the vax sysgen option. X>`009`009The bug was in the different byte/bit ordering on the X>`009`009various machines. This has been fixed. X> X>`009`009This version is NOT compatible with the original vax posting X>`009`009unless the '-DCOMPATIBLE' option is specified to the C X>`009`009compiler. The original posting has a bug which I fixed,`032 X>`009`009causing incompatible files. I recommend you NOT to use this X>`009`009option unless you already have a lot of packed files from X>`009`009the original posting by thomas. X>2.`009The exit status is not well defined (on some machines) causing the X>`009scripts to fail. X>`009`009The exit status is now 0,1 or 2 and is documented in X>`009`009compress.l. X>3.`009The function getopt() is not available in all C libraries. X>`009`009The function getopt() is no longer referenced by the X>`009`009program. X>4.`009Error status is not being checked on the fwrite() and fflush() calls. X>`009`009Fixed. X> X>The following enhancements have been made: X> X>1.`009Added facilities of "compact" into the compress program. "Pack", X>`009"Unpack", and "Pcat" are no longer required (no longer supplied). X>2.`009Installed work around for C compiler bug with "-O". X>3.`009Added a magic number header (\037\235). Put the bits specified X>`009in the file. X>4.`009Added "-f" flag to force overwrite of output file. X>5.`009Added "-c" flag and "zcat" program. 'ln compress zcat' after you X>`009compile. X>6.`009The 'uncompress' script has been deleted; simply`032 X>`009'ln compress uncompress' after you compile and it will work. X>7.`009Removed extra bit masking for machines that support unsigned X>`009characters. If your machine doesn't support unsigned characters, X>`009define "NO_UCHAR" when compiling. X> X>Compile "compress.c" with "-O -o compress" flags. Move "compress" to a X>standard executable location, such as /usr/local. Then: X>`009cd /usr/local X>`009ln compress uncompress X>`009ln compress zcat X> X>On machines that have a fixed stack size (such as Perkin-Elmer), set the X>stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). X> X>Next, install the manual (compress.l). X>`009cp compress.l /usr/man/manl`009`009- or - X>`009cp compress.l /usr/man/man1/compress.1 X> X>Here is the README that I sent with my first posting: X> X>>Enclosed is a modified version of compress.c, along with scripts to make it X>>run identically to pack(1), unpack(1), an pcat(1). Here is what I X>>(petsd!joe) and a colleague (petsd!peora!srd) did: X>> X>>1. Removed VAX dependencies. X>>2. Changed the struct to separate arrays; saves mucho memory. X>>3. Did comparisons in unsigned, where possible. (Faster on Perkin-Elmer.) X>>4. Sorted the character next chain and changed the search to stop X>>prematurely. This saves a lot on the execution time when compressing. X>> X>>This version is totally compatible with the original version. Even though X>>lint(1) -p has no complaints about compress.c, it won't run on a 16-bit X>>machine, due to the size of the arrays. X>> X>>Here is the README file from the original author: X>>`032 X>>>Well, with all this discussion about file compression (for news batching X>>>in particular) going around, I decided to implement the text compression X>>>algorithm described in the June Computer magazine. The author claimed X>>>blinding speed and good compression ratios. It's certainly faster than X>>>compact (but, then, what wouldn't be), but it's also the same speed as X>>>pack, and gets better compression than both of them. On 350K bytes of X>>>unix-wizards, compact took about 8 minutes of CPU, pack took about 80 X>>>seconds, and compress (herein) also took 80 seconds. But, compact and X>>>pack got about 30% compression, whereas compress got over 50%. So, I X>>>decided I had something, and that others might be interested, too. X>>> X>>>As is probably true of compact and pack (although I haven't checked), X>>>the byte order within a word is probably relevant here, but as long as X>>>you stay on a single machine type, you should be ok. (Can anybody X>>>elucidate on this?) There are a couple of asm's in the code (extv and X>>>insv instructions), so anyone porting it to another machine will have to X>>>deal with this anyway (and could probably make it compatible with Vax X>>>byte order at the same time). Anyway, I've linted the code (both with X>>>and without -p), so it should run elsewhere. Note the longs in the X>>>code, you can take these out if you reduce BITS to <= 15. X>>> X>>>Have fun, and as always, if you make good enhancements, or bug fixes, X>>>I'd like to see them. X>>> X>>>=Spencer (thomas@utah-20, `123harpo,hplabs,arizona`125!utah-cs!thomas) X>> X>>`009`009`009`009`009regards, X>>`009`009`009`009`009joe X>> X>>-- X>>Full-Name: Joseph M. Orost X>>UUCP: ..!`123decvax,ucbvax,ihnp4`125!vax135!petsd!joe X>>US Mail: MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724 X>>Phone: (201) 870-5844 $ GOSUB UNPACK_FILE $ FILE_IS = "COMPRESS.C" $ CHECKSUM_IS = 1745913017 $ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY X/*`032 X * Compress - data compression program`032 X */ X#define min(a,b)`009((a>b) ? b : a) X X/* X * machine variants which require cc -Dmachine:`009 pdp11, z8000, pcxt X */ X X/* X * Set USERMEM to the maximum amount of physical user memory available X * in bytes. USERMEM is used to determine the maximum BITS that can be used X * for compression. X * X * SACREDMEM is the amount of physical memory saved for others; compress X * will hog the rest. X */ X#ifndef SACREDMEM X#define SACREDMEM`0090 X#endif X X#ifndef USERMEM X# define USERMEM`009450000`009/* default user memory */ X#endif X X#ifdef interdata`009`009/* (Perkin-Elmer) */ X#define SIGNED_COMPARE_SLOW`009/* signed compare is slower than unsigned */ X#endif X X#ifdef pdp11 X# define BITS`00912`009/* max bits/code for 16-bit machine */ X# define NO_UCHAR`009/* also if "unsigned char" functions as signed char */ X# undef USERMEM`032 X#endif /* pdp11 */`009/* don't forget to compile with -i */ X X#ifdef z8000 X# define BITS`00912 X# undef vax`009`009/* weird preprocessor */ X# undef USERMEM`032 X#endif /* z8000 */ X X#ifdef MSDOS`009`009/* Microsoft C 3.0 for MS-DOS */ X# undef USERMEM X# ifdef BIG`009`009/* then this is a large data compilation */ X# undef DEBUG`009`009/* DEBUG makes the executible too big */ X# define BITS`009 16 X# define XENIX_16 X# else`009`009`009/* this is a small model compilation */ X# define BITS`009 12 X# endif X#else X#undef BIG X#endif /* MSDOS */ X X#ifdef pcxt X# define BITS`00912 X# undef USERMEM X#endif /* pcxt */ X X#ifdef USERMEM X# if USERMEM >= (433484+SACREDMEM) X# define PBITS 16 X# else X# if USERMEM >= (229600+SACREDMEM) X# define PBITS`00915 X# else X# if USERMEM >= (127536+SACREDMEM) X# define PBITS`00914 X# else X# if USERMEM >= (73464+SACREDMEM) X# define PBITS`00913 X# else X# define PBITS`00912 X# endif X# endif X# endif X# endif X# undef USERMEM X#endif /* USERMEM */ X X#ifdef PBITS`009`009/* Preferred BITS for this memory size */ X# ifndef BITS X# define BITS PBITS X# endif BITS X#endif /* PBITS */ X X#if BITS == 16 X# define HSIZE`00969001`009`009/* 95% occupancy */ X#endif X#if BITS == 15 X# define HSIZE`00935023`009`009/* 94% occupancy */ X#endif X#if BITS == 14 X# define HSIZE`00918013`009`009/* 91% occupancy */ X#endif X#if BITS == 13 X# define HSIZE`0099001`009`009/* 91% occupancy */ X#endif X#if BITS <= 12 X# define HSIZE`0095003`009`009/* 80% occupancy */ X#endif X X#ifdef M_XENIX`009`009`009/* Stupid compiler can't handle arrays with */ X# if BITS == 16`009`009`009/* more than 65535 bytes - so we fake it */ X# define XENIX_16 X# else X# if BITS > 13`009`009`009/* Code only handles BITS = 12, 13, or 16 */ X# define BITS 13 X# endif X# endif X#endif X X/* X * a code_int must be able to hold 2**BITS values of type int, and also -1 X */ X#if BITS > 15 Xtypedef long int`009code_int; X#else Xtypedef int`009`009code_int; X#endif X X#ifdef SIGNED_COMPARE_SLOW Xtypedef unsigned long int count_int; Xtypedef unsigned short int count_short; X#else Xtypedef long int`009 count_int; X#endif X X#ifdef NO_UCHAR X typedef char`009char_type; X#else X typedef`009unsigned char`009char_type; X#endif /* UCHAR */ Xchar_type magic_header[] = `123 "\037\235" `125;`009/* 1F 9D */ X X/* Defines for third byte of header */ X#define BIT_MASK`0090x1f X#define BLOCK_MASK`0090x80 X/* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is X a fourth header byte (for expansion). X*/ X#define INIT_BITS 9`009`009`009/* initial number of bits/code */ X X/* X * compress.c - File compression ala IEEE Computer, June 1984. X * X * Authors:`009Spencer W. Thomas`009(decvax!harpo!utah-cs!utah-gr!thomas) X *`009`009Jim McKie`009`009(decvax!mcvax!jim) X *`009`009Steve Davies`009`009(decvax!vax135!petsd!peora!srd) X *`009`009Ken Turkowski`009`009(decvax!decwrl!turtlevax!ken) X *`009`009James A. Woods`009`009(decvax!ihnp4!ames!jaw) X *`009`009Joe Orost`009`009(decvax!vax135!petsd!joe) X * X * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $ X * $Log:`009compress.c,v $ X * Revision 4.0.1 87/11/27 21:45:00 rms (Richard Stallman) X * Once copystat has run, don't delete output file on interrupt. X * Mention more flags in the Usage string. X * X * Revision 4.0`009 85/07/30 12:50:00 joe X * Removed ferror() calls in output routine on every output except first. X * Prepared for release to the world. X *`032 X * Revision 3.6`009 85/07/04 01:22:21 joe X * Remove much wasted storage by overlaying hash table with the tables X * used by decompress: tab_suffix[1<putc] and X * added signal catcher [plus beef in writeerr()] to delete effluvia. X * X * Revision 2.0`009 84/08/28 22:00:00 petsd!joe X * Add check for foreground before prompting user. Insert maxbits into X * compressed file. Force file being uncompressed to end with ".Z". X * Added "-c" flag and "zcat".`009Prepared for release. X * X * Revision 1.10 84/08/24 18:28:00 turtlevax!ken X * Will only compress regular files (no directories), added a magic number X * header (plus an undocumented -n flag to handle old files without headers), X * added -f flag to force overwriting of possibly existing destination file, X * otherwise the user is prompted for a response. Will tack on a .Z to a X * filename if it doesn't have one when decompressing.`009Will only replace X * file if it was compressed. X * X * Revision 1.9`009 84/08/16 17:28:00 turtlevax!ken X * Removed scanargs(), getopt(), added .Z extension and unlimited number of X * filenames to compress. Flags may be clustered (-Ddvb12) or separated X * (-D -d -v -b 12), or combination thereof. Modes and other status is X * copied with copystat(). -O bug for 4.2 seems to have disappeared with X * 1.8. X * X * Revision 1.8`009 84/08/09 23:15:00 joe X * Made it compatible with vax version, installed jim's fixes/enhancements X * X * Revision 1.6`009 84/08/01 22:08:00 joe X * Sped up algorithm significantly by sorting the compress chain. X * X * Revision 1.5`009 84/07/13 13:11:00 srd X * Added C version of vax asm routines.`009 Changed structure to arrays to X * save much memory. Do unsigned compares where possible (faster on X * Perkin-Elmer) X * X * Revision 1.4`009 84/07/05 03:11:11 thomas X * Clean up the code a little and lint it. (Lint complains about all X * the regs used in the asm, but I'm not going to "fix" this.) X * X * Revision 1.3`009 84/07/05 02:06:54 thomas X * Minor fixes. X * X * Revision 1.2`009 84/07/05 00:27:27 thomas X * Add variable bit length output. X * X */ Vstatic char rcs_ident[] = "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Re Xlease $"; X X#include X#include X#include X#include X#include X X#ifdef MSDOS X#include X#endif X X#define ARGVAL() (*++(*argv) `124`124 (--argc && *++argv)) X Xint n_bits;`009`009`009`009/* number of bits/code */ Xint maxbits = BITS;`009`009`009/* user settable max # bits/code */ Xcode_int maxcode;`009`009`009/* maximum code, given n_bits */ Vcode_int maxmaxcode = (code_int)1 << BITS; /* should NEVER generate this code X */ X#ifdef COMPATIBLE`009`009/* But wrong! */ X# define MAXCODE(n_bits)`009((code_int) 1 << (n_bits) - 1) X#else X# define MAXCODE(n_bits)`009(((code_int) 1 << (n_bits)) - 1) X#endif /* COMPATIBLE */ X X#ifdef XENIX_16 X# ifdef MSDOS X Xcount_int far htab0[8192]; Xcount_int far htab1[8192]; Xcount_int far htab2[8192]; Xcount_int far htab3[8192]; Xcount_int far htab4[8192]; Xcount_int far htab5[8192]; Xcount_int far htab6[8192]; Xcount_int far htab7[8192]; Xcount_int far htab8[HSIZE-65536]; Xcount_int far * htab[9] = `123 X`009htab0, htab1, htab2, htab3, htab4, htab5, htab6, htab7, htab8 `125; X Xunsigned short far code0tab[16384]; Xunsigned short far code1tab[16384]; Xunsigned short far code2tab[16384]; Xunsigned short far code3tab[16384]; Xunsigned short far code4tab[16384]; Xunsigned short far * codetab[5] = `123 X`009code0tab, code1tab, code2tab, code3tab, code4tab `125; X X# else X Xcount_int htab0[8192]; Xcount_int htab1[8192]; Xcount_int htab2[8192]; Xcount_int htab3[8192]; Xcount_int htab4[8192]; Xcount_int htab5[8192]; Xcount_int htab6[8192]; Xcount_int htab7[8192]; Xcount_int htab8[HSIZE-65536]; Xcount_int * htab[9] = `123 X`009htab0, htab1, htab2, htab3, htab4, htab5, htab6, htab7, htab8 `125; X Xunsigned short code0tab[16384]; Xunsigned short code1tab[16384]; Xunsigned short code2tab[16384]; Xunsigned short code3tab[16384]; Xunsigned short code4tab[16384]; Xunsigned short * codetab[5] = `123 X`009code0tab, code1tab, code2tab, code3tab, code4tab `125; X X# endif /* MSDOS */ X X#define htabof(i)`009(htab[(i) >> 13][(i) & 0x1fff]) X#define codetabof(i)`009(codetab[(i) >> 14][(i) & 0x3fff]) X X#else`009/* Normal machine */ Xcount_int htab [HSIZE]; Xunsigned short codetab [HSIZE]; X#define htabof(i)`009htab[i] X#define codetabof(i)`009codetab[i] X X#endif`009/* XENIX_16 */ Xcode_int hsize = HSIZE;`009`009`009/* for dynamic table sizing */ Xcount_int fsize; X X/* X * To save much memory, we overlay the table used by compress() with those X * used by decompress(). The tab_prefix table is the same size and type X * as the codetab. The tab_suffix table needs 2**BITS characters. We X * get this from the beginning of htab.`009 The output stack uses the rest X * of htab, and contains characters. There is plenty of room for any X * possible stack (stack used to be 8000 characters). X */ X X#define tab_prefixof(i) codetabof(i) X X#ifdef XENIX_16 X# ifdef MSDOS X# define tab_suffixof(i)`009((char_type far *)htab[(i)>>15])[(i) & 0x7fff] X# define de_stack`009`009((char_type far *)(htab2)) X# else X# define tab_suffixof(i)`009((char_type *)htab[(i)>>15])[(i) & 0x7fff] X# define de_stack`009`009((char_type *)(htab2)) X# endif /* MSDOS */ X#else`009/* Normal machine */ X# define tab_suffixof(i)`009((char_type *)(htab))[i] X# define de_stack`009`009((char_type *)&tab_suffixof((code_int)1< image (binary) mode; 2 <=> text mode */ X#else X# define PATH_SEP '/' X#endif X X#ifdef DEBUG Xint verbose = 0; X#endif /* DEBUG */ Xint (*bgnd_flag)(); X Xint do_decomp = 0; X X/***************************************************************** X * TAG( main ) X * X * Algorithm from "A Technique for High Performance Data Compression", X * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. X * X * Usage: compress [-cdfivV] [-b bits] [file ...] X * Inputs: X * X *`009-c:`009 Write output on stdout, don't remove original. X * X *`009-d:`009 If given, decompression is done instead. X * X *`009-f:`009 Forces output file to be generated, even if one already X *`009`009 exists, and even if no space is saved by compressing. X *`009`009 If -f is not used, the user will be prompted if stdin is X *`009`009 a tty, otherwise, the output file will not be overwritten. X * X *`009-i:`009 Image mode (defined only under MS-DOS). Prevents X *`009`009 conversion between UNIX text representation (LF line X *`009`009 termination) in compressed form and MS-DOS text X *`009`009 representation (CR-LF line termination) in uncompressed X *`009`009 form. Useful with non-text files. X * X *`009-v:`009 Write compression statistics X * X *`009-V:`009 Write version and compilation options. X * X *`009-b:`009 Parameter limits the max number of bits/code. X * X *`009file ...: Files to be compressed. If none specified, stdin X *`009`009 is used. X * Outputs: V *`009file.Z:`009 Compressed form of file with same mode, owner, and utime Xs X *`009or stdout (if stdin used as input) X * X * Assumptions: X *`009When filenames are given, replaces with the compressed version X *`009(.Z suffix) only if the file decreases in size. X * Algorithm: X *`009Modified Lempel-Ziv method (LZW). Basically finds common X * substrings and replaces them with a variable size code. This is X * deterministic, and can be done on the fly. Thus, the decompression X * procedure needs no input table, but tracks the way the table was built. X */ X Xmain( argc, argv ) Xregister int argc; char **argv; X`123 X int overwrite = 0;`009/* Do not overwrite unless given -f flag */ X char tempname[100]; X char **filelist, **fileptr; X char *cp, *rindex(), *malloc(); X struct stat statbuf; X extern onintr(); X X#ifdef MSDOS X char *sufp; X#else X extern oops(); X#endif X X#ifndef MSDOS X if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) `123 X#endif X X`009signal ( SIGINT, onintr ); X X#ifndef MSDOS X`009signal ( SIGSEGV, oops ); X `125 X#endif X X#ifdef COMPATIBLE X nomagic = 1;`009/* Original didn't have a magic number */ X#endif /* COMPATIBLE */ X X filelist = fileptr = (char **)(malloc(argc * sizeof(*argv))); X *filelist = NULL; X X if((cp = rindex(argv[0], PATH_SEP)) != 0) `123 X`009cp++; X `125 else `123 X`009cp = argv[0]; X `125 X X#ifdef MSDOS X if(strcmp(cp, "UNCOMPRE.EXE") == 0) `123 X#else X if(strcmp(cp, "uncompress") == 0) `123 X#endif X X`009do_decomp = 1; X`009 X#ifdef MSDOS X `125 else if(strcmp(cp, "ZCAT.EXE") == 0) `123 X#else X `125 else if(strcmp(cp, "zcat") == 0) `123 X#endif X X`009do_decomp = 1; X`009zcat_flg = 1; X `125 X X#ifdef BSD4_2 X /* 4.2BSD dependent - take it out if not */ X setlinebuf( stderr ); X#endif /* BSD4_2 */ X X /* Argument Processing X * All flags are optional. X * -D => debug X * -V => print Version; debug verbose X * -d => do_decomp X * -v => unquiet X * -f => force overwrite of output file X * -n => no header: useful to uncompress old files X * -b maxbits => maxbits. If -b is specified, then maxbits MUST be X *`009 given also. X * -c => cat all output to stdout X * -C => generate output compatible with compress 2.0. X * if a string is left, must be an input filename. X */ X for (argc--, argv++; argc > 0; argc--, argv++) `123 X`009if (**argv == '-') `123`009/* A flag argument */ X`009 while (*++(*argv)) `123`009/* Process all flags in this arg */ X`009`009switch (**argv) `123 X#ifdef DEBUG X`009`009 case 'D': X`009`009`009debug = 1; X`009`009`009break; X`009`009 case 'V': X`009`009`009verbose = 1; X`009`009`009version(); X`009`009`009break; X#else X`009`009 case 'V': X`009`009`009version(); X`009`009`009break; X#endif /* DEBUG */ X X#ifdef MSDOS X`009`009 case 'i': X`009`009`009image = 1; X`009`009`009break; X#endif X X`009`009 case 'v': X`009`009`009quiet = 0; X`009`009`009break; X`009`009 case 'd': X`009`009`009do_decomp = 1; X`009`009`009break; X`009`009 case 'f': X`009`009 case 'F': X`009`009`009overwrite = 1; X`009`009`009force = 1; X`009`009`009break; X`009`009 case 'n': X`009`009`009nomagic = 1; X`009`009`009break; X`009`009 case 'C': X`009`009`009block_compress = 0; X`009`009`009break; X`009`009 case 'b': X`009`009`009if (!ARGVAL()) `123 X`009`009`009 fprintf(stderr, "Missing maxbits\n"); X`009`009`009 Usage(); X`009`009`009 exit(1); X`009`009`009`125 X`009`009`009maxbits = atoi(*argv); X`009`009`009goto nextarg; X`009`009 case 'c': X`009`009`009zcat_flg = 1; X`009`009`009break; X`009`009 case 'q': X`009`009`009quiet = 1; X`009`009`009break; X`009`009 default: X`009`009`009fprintf(stderr, "Unknown flag: '%c'; ", **argv); X`009`009`009Usage(); X`009`009`009exit(1); X`009`009`125 X`009 `125 X`009`125 X`009else `123`009`009/* Input file name */ X`009 *fileptr++ = *argv; /* Build input file list */ X`009 *fileptr = NULL; X`009 /* process nextarg; */ X`009`125 X`009nextarg: continue; X `125 X X if(maxbits < INIT_BITS) maxbits = INIT_BITS; X if (maxbits > BITS) maxbits = BITS; X maxmaxcode = (code_int) 1 << maxbits; X X if (*filelist != NULL) `123 X`009for (fileptr = filelist; *fileptr; fileptr++) `123 X`009 exit_stat = 0; X`009 if (do_decomp != 0) `123`009`009`009/* DECOMPRESSION */ X X#ifdef MSDOS X`009`009/* Check for .Z or XZ suffix; add one if necessary */ X`009`009cp = *fileptr + strlen(*fileptr) - 2; X`009`009if ((*cp != '.' && *cp != 'X' && *cp != 'x') `124`124 X`009`009 (*(++cp) != 'Z' && *cp != 'z')) `123 X`009`009 strcpy(tempname, *fileptr); X`009`009 if ((cp=rindex(tempname,'.')) == NULL) X`009`009`009strcat(tempname, ".Z"); X`009`009 else if(*(++cp) == '\0') strcat(tempname, "Z"); X`009`009 else `123 X`009`009`009*(++cp) = '\0'; X`009`009`009strcat(tempname, "XZ"); X`009`009 `125 X`009`009 *fileptr = tempname; X`009`009`125 X#else X`009`009/* Check for .Z suffix */ X`009`009if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) `123 X`009`009 /* No .Z: tack one on */ X`009`009 strcpy(tempname, *fileptr); X`009`009 strcat(tempname, ".Z"); X`009`009 *fileptr = tempname; X`009`009`125 X#endif /*MSDOS */ X X`009`009/* Open input file for decompression */ X X#ifdef MSDOS X`009`009if ((freopen(*fileptr, "rb", stdin)) == NULL) `123 X#else X`009`009if ((freopen(*fileptr, "r", stdin)) == NULL) `123 X#endif X X`009`009`009perror(*fileptr); continue; X`009`009`125 X`009`009/* Check the magic number */ X`009`009if (nomagic == 0) `123 X`009`009 if ((getchar() != (magic_header[0] & 0xFF)) X`009`009 `124`124 (getchar() != (magic_header[1] & 0xFF))) `123 X`009`009`009fprintf(stderr, "%s: not in compressed format\n", X`009`009`009 *fileptr); X`009`009 continue; X`009`009 `125 X`009`009 maxbits = getchar();`009/* set -b from file */ X`009`009 block_compress = maxbits & BLOCK_MASK; X`009`009 maxbits &= BIT_MASK; X`009`009 maxmaxcode = (code_int) 1 << maxbits; X`009`009 if(maxbits > BITS) `123 X`009`009`009fprintf(stderr, X`009`009`009"%s: compressed with %d bits, can only handle %d bits\n", X`009`009`009*fileptr, maxbits, BITS); X`009`009`009continue; X`009`009 `125 X`009`009`125 X`009`009/* Generate output filename */ X`009`009strcpy(ofname, *fileptr); X`009`009ofname[strlen(*fileptr) - 2] = '\0'; /* Strip off .Z */ X`009 `125 else `123`009`009`009`009`009/* COMPRESSION */ X X#ifdef MSDOS X`009`009cp = *fileptr + strlen(*fileptr) - 2; X`009`009if ((*cp == '.' `124`124 *cp == 'X' `124`124 *cp == 'x') && X`009`009 (*(++cp) == 'Z' `124`124 *cp == 'z')) `123 X`009`009 fprintf(stderr,"%s: already has %s suffix -- no change\n", X`009`009`009*fileptr,--cp); X#else X`009`009if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) `123 X`009`009 fprintf(stderr, "%s: already has .Z suffix -- no change\n", X`009`009`009*fileptr); X#endif /* MSDOS */ X X`009`009 continue; X`009`009`125 X`009`009/* Open input file for compression */ X X#ifdef MSDOS X`009`009if ((freopen(*fileptr, image == 2 ? "rt" : "rb", stdin)) X`009`009 == NULL) `123 X#else X`009`009if ((freopen(*fileptr, "r", stdin)) == NULL) `123 X#endif X X`009`009 perror(*fileptr); continue; X`009`009`125 X`009`009stat ( *fileptr, &statbuf ); X`009`009fsize = (long) statbuf.st_size; X`009`009/* X`009`009 * tune hash table size for small files -- ad hoc, X`009`009 * but the sizes match earlier #defines, which X`009`009 * serve as upper bounds on the number of output codes.`032 X`009`009 */ X`009`009hsize = HSIZE; X`009`009if ( fsize < (1 << 12) ) X`009`009 hsize = min ( 5003, HSIZE ); X`009`009else if ( fsize < (1 << 13) ) X`009`009 hsize = min ( 9001, HSIZE ); X`009`009else if ( fsize < (1 << 14) ) X`009`009 hsize = min ( 18013, HSIZE ); X`009`009else if ( fsize < (1 << 15) ) X`009`009 hsize = min ( 35023, HSIZE ); X`009`009else if ( fsize < 47000 ) X`009`009 hsize = min ( 50021, HSIZE ); X X`009`009/* Generate output filename */ X`009`009strcpy(ofname, *fileptr); X#ifndef BSD4_2`009`009/* Short filenames */ X`009`009if ((cp = rindex(ofname, PATH_SEP)) != NULL) cp++; X`009`009else`009`009`009`009`009cp = ofname; X# ifdef MSDOS X`009`009if (zcat_flg == 0 && (sufp = rindex(cp, '.')) != NULL && X`009`009 strlen(sufp) > 2) fprintf(stderr, X`009`009 "%s: part of filename extension will be replaced by XZ\n", X`009`009 cp); X# else X`009`009if (strlen(cp) > 12) `123 X`009`009 fprintf(stderr,"%s: filename too long to tack on .Z\n",cp); X`009`009 continue; X`009`009`125 X# endif X#endif`009/* BSD4_2`009`009Long filenames allowed */ X`009`009`009`009`009`009`009`032 X#ifdef MSDOS X`009`009if ((cp = rindex(ofname, '.')) == NULL) strcat(ofname, ".Z"); X`009`009else `123 X`009`009 if(*(++cp) != '\0') *(++cp) = '\0'; X`009`009 strcat(ofname, "XZ"); X`009`009`125 X#else X`009`009strcat(ofname, ".Z"); X#endif /* MSDOS */ X X`009 `125 X`009 precious = 0; X`009 /* Check for overwrite of existing file */ X`009 if (overwrite == 0 && zcat_flg == 0) `123 X`009`009if (stat(ofname, &statbuf) == 0) `123 X`009`009 char response[2]; X`009`009 response[0] = 'n'; X`009`009 fprintf(stderr, "%s already exists;", ofname); X#ifndef MSDOS X`009`009 if (foreground()) `123 X#endif X`009`009`009fprintf(stderr, X`009`009`009 " do you wish to overwrite %s (y or n)? ", ofname); X`009`009`009fflush(stderr); X`009`009`009read(2, response, 2); X`009`009`009while (response[1] != '\n') `123 X`009`009`009 if (read(2, response+1, 1) < 0) `123`009/* Ack! */ X`009`009`009`009perror("stderr"); break; X`009`009`009 `125 X`009`009`009`125 X#ifndef MSDOS X`009`009 `125 X#endif X`009`009 if (response[0] != 'y') `123 X`009`009`009fprintf(stderr, "\tnot overwritten\n"); X`009`009`009continue; X`009`009 `125 X`009`009`125 X`009 `125 X`009 if(zcat_flg == 0) `123`009`009/* Open output file */ X X#ifdef MSDOS X`009`009if (freopen(ofname, do_decomp && image == 2 ? "wt" : "wb", X`009`009 stdout) == NULL) `123 X#else`009`009`032 X`009`009if (freopen(ofname, "w", stdout) == NULL) `123 X#endif X X`009`009 perror(ofname); continue; X`009`009`125 X`009`009if(!quiet) X`009`009`009fprintf(stderr, "%s: ", *fileptr); X`009 `125 X X`009 /* Actually do the compression/decompression */ X`009 if (do_decomp == 0) compress(); X#ifndef DEBUG X`009 else`009`009`009decompress(); X#else X`009 else if (debug == 0)`009decompress(); X`009 else`009`009`009printcodes(); X`009 if (verbose)`009`009dump_tab(); X#endif /* DEBUG */ X`009 if(zcat_flg == 0) `123 X`009`009copystat(*fileptr, ofname);`009/* Copy stats */ X`009`009if((exit_stat == 1) `124`124 (!quiet)) X`009`009`009putc('\n', stderr); X`009 `125 X`009`125 X `125 else `123`009`009/* Standard input */ X`009if (do_decomp == 0) `123 X`009`009compress(); X#ifdef DEBUG X`009`009if(verbose)`009`009dump_tab(); X#endif /* DEBUG */ X`009`009if(!quiet) X`009`009`009putc('\n', stderr); X`009`125 else `123 X`009 /* Check the magic number */ X`009 if (nomagic == 0) `123 X`009`009if ((getchar()!=(magic_header[0] & 0xFF)) X`009`009 `124`124 (getchar()!=(magic_header[1] & 0xFF))) `123 X`009`009 fprintf(stderr, "stdin: not in compressed format\n"); X`009`009 exit(1); X`009`009`125 X`009`009maxbits = getchar();`009/* set -b from file */ X`009`009block_compress = maxbits & BLOCK_MASK; X`009`009maxbits &= BIT_MASK; X`009`009maxmaxcode = (code_int) 1 << maxbits; X`009`009fsize = 100000;`009`009/* assume stdin large for USERMEM */ X`009`009if(maxbits > BITS) `123 X`009`009`009fprintf(stderr, X`009`009`009"stdin: compressed with %d bits, can only handle %d bits\n", X`009`009`009maxbits, BITS); X`009`009`009exit(1); X`009`009`125 X`009 `125 X#ifndef DEBUG X`009 decompress(); X#else X`009 if (debug == 0)`009decompress(); X`009 else`009`009printcodes(); X`009 if (verbose)`009dump_tab(); X#endif /* DEBUG */ X`009`125 X `125 X exit(exit_stat); X`125 X Xstatic int offset; Xlong int in_count = 1;`009`009`009/* length of input */ Xlong int bytes_out;`009`009`009/* length of compressed output */ Xlong int out_count = 0;`009`009`009/* # of codes output (for debugging) */ X X/* X * compress stdin to stdout X * X * Algorithm: use open addressing double hashing (no chaining) on the`032 X * prefix code / next character combination. We do a variant of Knuth's X * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime X * secondary probe. Here, the modular division first probe is gives way X * to a faster exclusive-or manipulation. Also do block compression with X * an adaptive reset, whereby the code table is cleared when the compression X * ratio decreases, but after the table fills.`009The variable-length output X * codes are re-sized at this point, and a special CLEAR code is generated X * for the decompressor. Late addition: construct the table according to X * file size for noticeable speed improvement on small files. Please direct X * questions about this implementation to ames!jaw. X */ X Xcompress() `123 X register long fcode; X register code_int i = 0; X register int c; X register code_int ent; X register code_int disp; X register code_int hsize_reg; X register int hshift; X X#ifndef COMPATIBLE X if (nomagic == 0) `123 X`009putchar(magic_header[0]); putchar(magic_header[1]); X`009putchar((char)(maxbits `124 block_compress)); X`009if(ferror(stdout)) X`009`009writeerr(); X `125 X#endif /* COMPATIBLE */ X X offset = 0; X bytes_out = 3;`009`009/* includes 3-byte header mojo */ X out_count = 0; X clear_flg = 0; X ratio = 0; X in_count = 1; X checkpoint = CHECK_GAP; X maxcode = MAXCODE(n_bits = INIT_BITS); X free_ent = ((block_compress) ? FIRST : 256 ); X X ent = getchar (); X X hshift = 0; X for ( fcode = (long) hsize;`009 fcode < 65536L; fcode *= 2L ) X`009hshift++; X hshift = 8 - hshift;`009`009/* set hash code range bound */ X X hsize_reg = hsize; X cl_hash( (count_int) hsize_reg);`009`009/* clear hash table */ X X#ifdef SIGNED_COMPARE_SLOW X while ( (c = getchar()) != (unsigned) EOF ) `123 X#else X while ( (c = getchar()) != EOF ) `123 X#endif X X#ifdef MSDOS X`009if (c == '\n') in_count += image; else /* include CR if text mode */ X#endif X X`009in_count++; X X`009fcode = (long) (((long) c << maxbits) + ent); X`009i = (((code_int)c << hshift) `094 ent);`009/* xor hashing */ X X`009if ( htabof (i) == fcode ) `123 X`009 ent = codetabof (i); X`009 continue; X`009`125 else if ( (long)htabof (i) < 0 )`009/* empty slot */ X`009 goto nomatch; X`009disp = hsize_reg - i;`009`009/* secondary hash (after G. Knott) */ X`009if ( i == 0 ) X`009 disp = 1; Xprobe: X`009if ( (i -= disp) < 0 ) X`009 i += hsize_reg; X X`009if ( htabof (i) == fcode ) `123 X`009 ent = codetabof (i); X`009 continue; X`009`125 X`009if ( (long)htabof (i) > 0 )`032 X`009 goto probe; Xnomatch: X`009output ( (code_int) ent ); X`009out_count++; X`009ent = c; X#ifdef SIGNED_COMPARE_SLOW X`009if ( (unsigned) free_ent < (unsigned) maxmaxcode) `123 X#else X`009if ( free_ent < maxmaxcode ) `123 X#endif X`009 codetabof (i) = free_ent++; /* code -> hashtable */ X`009 htabof (i) = fcode; X`009`125 X`009else if ( (count_int)in_count >= checkpoint && block_compress ) X`009 cl_block (); X `125 X /* X * Put out the final code. X */ X output( (code_int)ent ); X out_count++; X output( (code_int)-1 ); X X /* X * Print out stats on stderr X */ X if(zcat_flg == 0 && !quiet) `123 X#ifdef DEBUG X`009fprintf( stderr, X`009`009"%ld chars in, %ld codes (%ld bytes) out, compression factor: ", X`009`009in_count, out_count, bytes_out ); X`009prratio( stderr, in_count, bytes_out ); X`009fprintf( stderr, "\n"); X`009fprintf( stderr, "\tCompression as in compact: " ); X`009prratio( stderr, in_count-bytes_out, in_count ); X`009fprintf( stderr, "\n"); X`009fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n", X`009`009free_ent - 1, n_bits ); X#else /* !DEBUG */ X`009fprintf( stderr, "Compression: " ); X`009prratio( stderr, in_count-bytes_out, in_count ); X#endif /* DEBUG */ X `125 X if(bytes_out > in_count)`009/* exit(2) if no savings */ X`009exit_stat = 2; X return; X`125 X X/***************************************************************** X * TAG( output ) X * X * Output the given code. X * Inputs: X *`009code:`009A n_bits-bit integer. If == -1, then EOF. This assumes X *`009`009that n_bits =< (long)wordsize - 1. X * Outputs: X *`009Outputs code to the file. X * Assumptions: X *`009Chars are 8 bits long. X * Algorithm: X *`009Maintain a BITS character long buffer (so that 8 codes will X * fit in it exactly).`009Use the VAX insv instruction to insert each X * code in turn. When the buffer fills up empty it and start over. X */ X Xstatic char buf[BITS]; X X#ifndef vax Vchar_type lmask[9] = `1230xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 X`125; Vchar_type rmask[9] = `1230x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff X`125; X#endif /* vax */ X Xoutput( code ) Xcode_int code; X`123 X#ifdef DEBUG X static int col = 0; X#endif /* DEBUG */ X X /* X * On the VAX, it is important to have the register declarations X * in exactly the order given, or the asm will break. X */ X register int r_off = offset, bits= n_bits; X register char * bp = buf; X X#ifdef DEBUG X`009if ( verbose ) X`009 fprintf( stderr, "%5d%c", code, X`009`009 (col+=6) >= 74 ? (col = 0, '\n') : ' ' ); X#endif /* DEBUG */ X if ( code >= 0 ) `123 X#ifdef vax X`009/* VAX DEPENDENT!! Implementation on other machines is below. X`009 * X`009 * Translation: Insert BITS bits from the argument starting at X`009 * offset bits from the beginning of buf. X`009 */ X`0090;`009/* Work around for pcc -O bug with asm and if stmt */ X`009asm( "insv`0094(ap),r11,r10,(r9)" ); X#else /* not a vax */ X/*`032 X * byte/bit numbering on the VAX is simulated by the following code X */ X`009/* X`009 * Get to the first byte. X`009 */ X`009bp += (r_off >> 3); X`009r_off &= 7; X`009/* X`009 * Since code is always >= 8 bits, only need to mask the first X`009 * hunk on the left. X`009 */ X`009*bp = (*bp & rmask[r_off]) `124 (code << r_off) & lmask[r_off]; X`009bp++; X`009bits -= (8 - r_off); X`009code >>= 8 - r_off; X`009/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ X`009if ( bits >= 8 ) `123 X`009 *bp++ = code; X`009 code >>= 8; X`009 bits -= 8; X`009`125 X`009/* Last bits. */ X`009if(bits) X`009 *bp = code; X#endif /* vax */ X`009offset += n_bits; X`009if ( offset == (n_bits << 3) ) `123 X`009 bp = buf; X`009 bits = n_bits; X`009 bytes_out += bits; X`009 do X`009`009putchar(*bp++); X`009 while(--bits); X`009 offset = 0; X`009`125 X X`009/* X`009 * If the next entry is going to be too big for the code size, X`009 * then increase it, if possible. X`009 */ X`009if ( free_ent > maxcode `124`124 (clear_flg > 0)) X`009`123 X`009 /* X`009 * Write the whole buffer, because the input side won't X`009 * discover the size increase until after it has read it. X`009 */ X`009 if ( offset > 0 ) `123 X`009`009if( fwrite( buf, 1, n_bits, stdout ) != n_bits) X`009`009`009writeerr(); X`009`009bytes_out += n_bits; X`009 `125 X`009 offset = 0; X X`009 if ( clear_flg ) `123 X`009`009maxcode = MAXCODE (n_bits = INIT_BITS); X`009`009clear_flg = 0; X`009 `125 X`009 else `123 X`009`009n_bits++; X`009`009if ( n_bits == maxbits ) X`009`009 maxcode = maxmaxcode; X`009`009else X`009`009 maxcode = MAXCODE(n_bits); X`009 `125 X#ifdef DEBUG X`009 if ( debug ) `123 X`009`009fprintf( stderr, "\nChange to %d bits\n", n_bits ); X`009`009col = 0; X`009 `125 X#endif /* DEBUG */ X`009`125 X `125 else `123 X`009/* X`009 * At EOF, write the rest of the buffer. X`009 */ X`009if ( offset > 0 ) X`009 fwrite( buf, 1, (offset + 7) / 8, stdout ); X`009bytes_out += (offset + 7) / 8; X`009offset = 0; X`009fflush( stdout ); X#ifdef DEBUG X`009if ( verbose ) X`009 fprintf( stderr, "\n" ); X#endif /* DEBUG */ X`009if( ferror( stdout ) ) X`009`009writeerr(); X `125 X`125 X X/* X * Decompress stdin to stdout.`009This routine adapts to the codes in the X * file building the "string" table on-the-fly; requiring no table to X * be stored in the compressed file. The tables used herein are shared X * with those of the compress() routine. See the definitions above. X */ X Xdecompress() `123 X X#ifdef BIG X register char_type far *stackp; X#else X register char_type *stackp; X#endif X X register int finchar; X register code_int code, oldcode, incode; X X /* X * As above, initialize the first 256 entries in the table. X */ X maxcode = MAXCODE(n_bits = INIT_BITS); X for ( code = 255; code >= 0; code-- ) `123 X`009tab_prefixof(code) = 0; X`009tab_suffixof(code) = (char_type)code; X `125 X free_ent = ((block_compress) ? FIRST : 256 ); X X finchar = oldcode = getcode(); X if(oldcode == -1)`009/* EOF already? */ X`009return;`009`009`009/* Get out of here */ X putchar( (char)finchar );`009`009/* first code must be 8 bits = char */ X if(ferror(stdout))`009`009/* Crash if can't write */ X`009writeerr(); X stackp = de_stack; X X while ( (code = getcode()) > -1 ) `123 X X`009if ( (code == CLEAR) && block_compress ) `123 X`009 for ( code = 255; code >= 0; code-- ) X`009`009tab_prefixof(code) = 0; X`009 clear_flg = 1; X`009 free_ent = FIRST - 1; X`009 if ( (code = getcode ()) == -1 )`009/* O, untimely death! */ X`009`009break; X`009`125 X`009incode = code; X`009/* X`009 * Special case for KwKwK string. X`009 */ X`009if ( code >= free_ent ) `123 X`009 *stackp++ = finchar; X`009 code = oldcode; X`009`125 X X`009/* X`009 * Generate output characters in reverse order X`009 */ X#ifdef SIGNED_COMPARE_SLOW X`009while ( ((unsigned long)code) >= ((unsigned long)256) ) `123 X#else X`009while ( code >= 256 ) `123 X#endif X`009 *stackp++ = tab_suffixof(code); X`009 code = tab_prefixof(code); X`009`125 X`009*stackp++ = finchar = tab_suffixof(code); X X`009/* X`009 * And put them out in forward order X`009 */ X`009do X`009 putchar ( *--stackp ); X`009while ( stackp > de_stack ); X X`009/* X`009 * Generate the new entry. X`009 */ X`009if ( (code=free_ent) < maxmaxcode ) `123 X`009 tab_prefixof(code) = (unsigned short)oldcode; X`009 tab_suffixof(code) = finchar; X`009 free_ent = code+1; X`009`125`032 X`009/* X`009 * Remember previous code. X`009 */ X`009oldcode = incode; X `125 X fflush( stdout ); X if(ferror(stdout)) X`009writeerr(); X`125 X X/***************************************************************** X * TAG( getcode ) X * X * Read one code from the standard input. If EOF, return -1. X * Inputs: X *`009stdin X * Outputs: X *`009code or -1 is returned. X */ X Xcode_int Xgetcode() `123 X /* X * On the VAX, it is important to have the register declarations X * in exactly the order given, or the asm will break. X */ X register code_int code; X static int offset = 0, size = 0; X static char_type buf[BITS]; X register int r_off, bits; X register char_type *bp = buf; X V if ( clear_flg > 0 `124`124 offset >= size `124`124 free_ent > maxcode ) X `123 X`009/* X`009 * If the next entry will be too big for the current code X`009 * size, then we must increase the size. This implies reading X`009 * a new buffer full, too. X`009 */ X`009if ( free_ent > maxcode ) `123 X`009 n_bits++; X`009 if ( n_bits == maxbits ) X`009`009maxcode = maxmaxcode;`009/* won't get any bigger now */ X`009 else X`009`009maxcode = MAXCODE(n_bits); X`009`125 X`009if ( clear_flg > 0) `123 X`009 maxcode = MAXCODE (n_bits = INIT_BITS); X`009 clear_flg = 0; X`009`125 X`009size = fread( buf, 1, n_bits, stdin ); X`009if ( size <= 0 ) X`009 return -1;`009`009`009/* end of file */ X`009offset = 0; X`009/* Round size down to integral number of codes */ X`009size = (size << 3) - (n_bits - 1); X `125 X r_off = offset; X bits = n_bits; X#ifdef vax X asm( "extzv`009 r10,r9,(r8),r11" ); X#else /* not a vax */ X`009/* X`009 * Get to the first byte. X`009 */ X`009bp += (r_off >> 3); X`009r_off &= 7; X`009/* Get first part (low order bits) */ X#ifdef NO_UCHAR X`009code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff; X#else X`009code = (*bp++ >> r_off); X#endif /* NO_UCHAR */ X`009bits -= (8 - r_off); X`009r_off = 8 - r_off;`009`009/* now, offset into code word */ X`009/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ X`009if ( bits >= 8 ) `123 X#ifdef NO_UCHAR X`009 code `124= (*bp++ & 0xff) << r_off; X#else X`009 code `124= *bp++ << r_off; X#endif /* NO_UCHAR */ X`009 r_off += 8; X`009 bits -= 8; X`009`125 X`009/* high order bits. */ X`009code `124= (*bp & rmask[bits]) << r_off; X#endif /* vax */ X offset += n_bits; X X return code; X`125 X Xchar * Xrindex(s, c)`009`009/* For those who don't have it in libc.a */ Xregister char *s, c; X`123 X`009char *p; X`009for (p = NULL; *s; s++) X`009 if (*s == c) X`009`009p = s; X`009return(p); X`125 X X#ifdef DEBUG Xprintcodes() X`123 X /* X * Just print out codes from input file. For debugging. X */ X code_int code; X int col = 0, bits; X X bits = n_bits = INIT_BITS; X maxcode = MAXCODE(n_bits); X free_ent = ((block_compress) ? FIRST : 256 ); X while ( ( code = getcode() ) >= 0 ) `123 X`009if ( (code == CLEAR) && block_compress ) `123 X`009 free_ent = FIRST - 1; X`009 clear_flg = 1; X`009`125 X`009else if ( free_ent < maxmaxcode ) X`009 free_ent++; X`009if ( bits != n_bits ) `123 X`009 fprintf(stderr, "\nChange to %d bits\n", n_bits ); X`009 bits = n_bits; X`009 col = 0; X`009`125 X`009fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' ); X `125 X putc( '\n', stderr ); X exit( 0 ); X`125 X Xcode_int sorttab[1<= 0) `123 X`009`009`009sorttab[codetabof(i)] = i; X`009`009`125 X`009`125 X`009first = block_compress ? FIRST : 256; X`009for(i = first; i < free_ent; i++) `123 X`009`009fprintf(stderr, "%5d: \"", i); X`009`009de_stack[--stack_top] = '\n'; X`009`009de_stack[--stack_top] = '"'; X`009`009stack_top = in_stack((htabof(sorttab[i])>>maxbits)&0xff,`032 X`009`009`009`009 stack_top); X`009`009for(ent=htabof(sorttab[i]) & ((1< 256; X`009`009 ent=htabof(sorttab[ent]) & ((1<> maxbits, X`009`009`009`009`009`009stack_top); X`009`009`125 X`009`009stack_top = in_stack(ent, stack_top); X`009`009fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr); X`009`009stack_top = STACK_SIZE; X`009`125 X `125 else if(!debug) `123`009/* decompressing */ X X for ( i = 0; i < free_ent; i++ ) `123 X`009 ent = i; X`009 c = tab_suffixof(ent); X`009 if ( isascii(c) && isprint(c) ) X`009 fprintf( stderr, "%5d: %5d/'%c'`009\"", X`009`009`009 ent, tab_prefixof(ent), c ); X`009 else X`009 fprintf( stderr, "%5d: %5d/\\%03o \"", X`009`009`009 ent, tab_prefixof(ent), c ); X`009 de_stack[--stack_top] = '\n'; X`009 de_stack[--stack_top] = '"'; X`009 for ( ; ent != NULL; X`009`009 ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) `123 X`009 stack_top = in_stack(tab_suffixof(ent), stack_top); X`009 `125 X`009 fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr ); X`009 stack_top = STACK_SIZE; X `125 X `125 X`125 X Xint Xin_stack(c, stack_top) X`009register c, stack_top; X`123 X`009if ( (isascii(c) && isprint(c) && c != '\\') `124`124 c == ' ' ) `123 X`009 de_stack[--stack_top] = c; X`009`125 else `123 X`009 switch( c ) `123 X`009 case '\n': de_stack[--stack_top] = 'n'; break; X`009 case '\t': de_stack[--stack_top] = 't'; break; X`009 case '\b': de_stack[--stack_top] = 'b'; break; X`009 case '\f': de_stack[--stack_top] = 'f'; break; X`009 case '\r': de_stack[--stack_top] = 'r'; break; X`009 case '\\': de_stack[--stack_top] = '\\'; break; X`009 default: X`009`009de_stack[--stack_top] = '0' + c % 8; X`009`009de_stack[--stack_top] = '0' + (c / 8) % 8; X`009`009de_stack[--stack_top] = '0' + c / 64; X`009`009break; X`009 `125 X`009 de_stack[--stack_top] = '\\'; X`009`125 X`009return stack_top; X`125 X#endif /* DEBUG */ X Xwriteerr() X`123 X perror ( ofname ); X unlink ( ofname ); X exit ( 1 ); X`125 X Xcopystat(ifname, ofname) Xchar *ifname, *ofname; X`123 X struct stat statbuf; X int mode; X time_t timep[2]; X X#ifdef MSDOS V if (_osmajor < 3) freopen("CON","at",stdout); else`009 /* MS-DOS 2.xx bu Xg */ X#endif X X fclose(stdout); X if (stat(ifname, &statbuf)) `123`009`009/* Get stat on input file */ X`009perror(ifname); X`009return; X `125 X X#ifndef MSDOS X if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) `123 X`009if(quiet) X`009`009fprintf(stderr, "%s: ", ifname); X`009fprintf(stderr, " -- not a regular file: unchanged"); X`009exit_stat = 1; X `125 else if (statbuf.st_nlink > 1) `123 X`009if(quiet) X`009`009fprintf(stderr, "%s: ", ifname); X`009fprintf(stderr, " -- has %d other links: unchanged", X`009`009statbuf.st_nlink - 1); X`009exit_stat = 1; V `125 else if (exit_stat == 2 && (!force)) `123 /* No compression: remove X file.Z */ X#else X if (exit_stat == 2 && (!force)) `123 /* No compression: remove file.Z */ X#endif /* MSDOS */ X X`009if(!quiet) X`009`009fprintf(stderr, " -- file unchanged"); X `125 else `123`009`009`009/* ***** Successful Compression ***** */ X`009exit_stat = 0; X`009mode = statbuf.st_mode & 07777; X`009if (chmod(ofname, mode))`009`009/* Copy modes */ X`009 perror(ofname); X X#ifndef MSDOS X`009chown(ofname, statbuf.st_uid, statbuf.st_gid);`009/* Copy ownership */ X#endif X X`009timep[0] = statbuf.st_atime; X`009timep[1] = statbuf.st_mtime; X`009utime(ofname, timep);`009/* Update last accessed and modified times */ X`009precious = 1; X`009if (unlink(ifname))`009/* Remove input file */ X`009 perror(ifname); X`009if(!quiet) X`009`009fprintf(stderr, " -- replaced with %s", ofname); X`009return;`009`009/* Successful return */ X `125 X X /* Unsuccessful return -- one of the tests failed */ X if (unlink(ofname)) X`009perror(ofname); X`125 X X#ifndef MSDOS X/* X * This routine returns 1 if we are running in the foreground and stderr X * is a tty. X */ Xforeground() X`123 X`009if(bgnd_flag != SIG_DFL) `123 /* background? */ X`009`009return(0); X`009`125 else `123`009`009`009/* foreground */ X`009`009if(isatty(2)) `123`009`009/* and stderr is a tty */ X`009`009`009return(1); X`009`009`125 else `123 X`009`009`009return(0); X`009`009`125 X`009`125 X`125 X#endif X Xonintr ( ) X`123 X if (!precious) X`009unlink ( ofname ); X exit ( 1 ); X`125 X X#ifndef MSDOS Xoops ( )`009/* wild pointer -- assume bad input */ X`123 X if ( do_decomp == 1 )`032 X`009fprintf ( stderr, "uncompress: corrupt input\n" ); X unlink ( ofname ); X exit ( 1 ); X`125 X#endif /* MSDOS */ X Xcl_block ()`009`009/* table clear for block compress */ X`123 X register long int rat; X X checkpoint = in_count + CHECK_GAP; X#ifdef DEBUG X`009if ( debug ) `123 X`009`009fprintf ( stderr, "count: %ld, ratio: ", in_count ); X`009`009prratio ( stderr, in_count, bytes_out ); X`009`009fprintf ( stderr, "\n"); X`009`125 X#endif /* DEBUG */ X X if(in_count > 0x007fffff) `123 /* shift will overflow */ X`009rat = bytes_out >> 8; X`009if(rat == 0) `123`009`009/* Don't divide by zero */ X`009 rat = 0x7fffffff; X`009`125 else `123 X`009 rat = in_count / rat; X`009`125 X `125 else `123 X`009rat = (in_count << 8) / bytes_out;`009/* 8 fractional bits */ X `125 X if ( rat > ratio ) `123 X`009ratio = rat; X `125 else `123 X`009ratio = 0; X#ifdef DEBUG X`009if(verbose) X`009`009dump_tab();`009/* dump string table */ X#endif X`009cl_hash ( (count_int) hsize ); X`009free_ent = FIRST; X`009clear_flg = 1; X`009output ( (code_int) CLEAR ); X#ifdef DEBUG X`009if(debug) X`009`009fprintf ( stderr, "clear\n" ); X#endif /* DEBUG */ X `125 X`125 X Xcl_hash(hsize)`009`009/* reset code table */ X`009register count_int hsize; X`123 X X#ifdef XENIX_16 X`009register j; X`009register long k = hsize; X`009`032 X# ifdef MSDOS X`009register count_int far *htab_p; X# else X`009register count_int *htab_p; X# endif /* MSDOS */ X X#else`009/* Normal machine */ X`009register count_int *htab_p = htab+hsize; X#endif`009/* XENIX_16 */ X X`009register long i; X`009register long m1 = -1; X X#ifdef XENIX_16 X for(j=0; j<=8 && k>=0; j++,k-=8192) `123 X`009i = 8192; X`009if(k < 8192) `123 X`009 i = k; X`009`125 X`009htab_p = &(htab[j][i]); X`009i -= 16; X`009if(i > 0) `123 X#else X`009i = hsize - 16; X#endif X`009do `123`009`009`009`009/* might use Sys V memset(3) here */ X`009`009*(htab_p-16) = m1; X`009`009*(htab_p-15) = m1; X`009`009*(htab_p-14) = m1; X`009`009*(htab_p-13) = m1; X`009`009*(htab_p-12) = m1; X`009`009*(htab_p-11) = m1; X`009`009*(htab_p-10) = m1; X`009`009*(htab_p-9) = m1; X`009`009*(htab_p-8) = m1; X`009`009*(htab_p-7) = m1; X`009`009*(htab_p-6) = m1; X`009`009*(htab_p-5) = m1; X`009`009*(htab_p-4) = m1; X`009`009*(htab_p-3) = m1; X`009`009*(htab_p-2) = m1; X`009`009*(htab_p-1) = m1; X`009`009htab_p -= 16; X`009`125 while ((i -= 16) >= 0); X#ifdef XENIX_16 X`009`125 X `125 X#endif X`009for ( i += 16; i > 0; i-- ) X`009`009*--htab_p = m1; X`125 X Xprratio(stream, num, den) XFILE *stream; Xlong int num, den; X`123 X X#ifdef DEBUG X`009register long q;`009`009/* permits `124result`124 > 655.36% */ X#else X`009register int q;`009`009`009/* Doesn't need to be long */ X#endif X X`009if(num > 214748L) `123`009`009/* 2147483647/10000 */ X`009`009q = num / (den / 10000L); X`009`125 else `123 X`009`009q = 10000L * num / den;`009`009/* Long calculations, though */ X`009`125 X`009if (q < 0) `123 X`009`009putc('-', stream); X`009`009q = -q; X`009`125 X`009fprintf(stream, "%d.%02d%%", (int)(q / 100), (int)(q % 100)); X`125 X Xversion() X`123 X`009fprintf(stderr, "%s\n", rcs_ident); X`009fprintf(stderr, "Options: "); X#ifdef vax X`009fprintf(stderr, "vax, "); X#endif X#ifdef NO_UCHAR X`009fprintf(stderr, "NO_UCHAR, "); X#endif X#ifdef SIGNED_COMPARE_SLOW X`009fprintf(stderr, "SIGNED_COMPARE_SLOW, "); X#endif X#ifdef MSDOS X`009fprintf(stderr, "MSDOS, "); X#endif X#ifdef XENIX_16 X`009fprintf(stderr, "XENIX_16, "); X#endif X#ifdef COMPATIBLE X`009fprintf(stderr, "COMPATIBLE, "); X#endif X#ifdef DEBUG X`009fprintf(stderr, "DEBUG, "); X#endif X#ifdef BSD4_2 X`009fprintf(stderr, "BSD4_2, "); X#endif X`009fprintf(stderr, "BITS = %d\n", BITS); X`125 $ GOSUB UNPACK_FILE $ EXIT