From:	CSBVAX::MRGATE!ijah400%ivax.decnet@gold.bacs.indiana.edu@SMTP 16-AUG-1988 22:18
To:	ARISIA::EVERHART
Subj:	lzcomp/lzdcmp part 2 of 6.


Date: 16 Aug 88 15:20:00 EST
From: "IVAX::IJAH400" <ijah400%ivax.decnet@gold.bacs.indiana.edu>
Subject: lzcomp/lzdcmp part 2 of 6.
To: "everhart%arisia.decnet" <everhart%arisia.decnet@ge-crd.arpa>

$Part2:
$ File_is="LZCMP1.C"
$ Check_Sum_is=1752402719
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X/*
X *`009`009lzcomp [-options] infile outfile
X */
X#ifdef`009DOCUMENTATION
X
Xtitle`009lzcomp`009File Compression
Xindex`009`009File compression
X
Xsynopsis
X`009.s.nf
X`009lzcomp [-options] [infile [outfile]]
X`009.s.f
Xdescription
X
X`009lzcomp implements the Lempel-Ziv file compression algorithm.
X`009(Files compressed by lzcomp are uncompressed by lzdcmp.)
X`009It operates by finding common substrings and replaces them
X`009with a variable-size code.  This is deterministic, and
X`009can be done with a single pass over the file.  Thus,
X`009the decompression procedure needs no input table, but
X`009can track the way the table was built.
X
X`009Options may be given in either case.
X`009.lm +8
X`009.p -8
X`009-B`009Input file is "binary", not "human readable text".
X`009This is necessary on Dec operating systems, such as VMS and
X`009RSX-11M, that treat these files differently.  (Note that binary
X`009support is rudamentary and probably insufficient as yet.)
X`009(On VMS version 4, this is ignored unless the -x option is
X`009specified or the input file is record-oriented.)
X`009.p -8
X`009-M bits`009Write using the specified number of bits in the
X`009code -- necessary for big machines making files for little
X`009machines.  For example, if compressing a file on VMS
X`009which is to be read on a PDP-11, you should select -M 12.
X`009.p -8
X`009-V [n]`009Verbose if specified.  If a value is specified,
X`009it will enable debugging code (if compiled in).
X`009.p -8
X`009-X [n]`009"Export" -- write a file format that can be read by
X`009other operating systems.  Only the bytes in the file are copied;
X`009file attributes are not preserved.  If specified, the value
X`009determines the level of compatiblity.  If not specified,
X`009or specified with an explicit value of zero, and lzcomp is
X`009running on Vax/VMS version 4 under VaxC and the input file
X`009is a disk or magtape file (block-oriented), a VMS-private output
X`009format is used which is incompatible with the Unix compress
X`009utility, but which preserves VMS file attributes.  -X may
X`009take on the following values:
X`009.lm +4.s
X`009.i -4;#0##Choose VMS private format.  See restrictions below.
X`009.i -4;#1##Compatible with Unix compress version 3.0:
X`009this is the default if -x is given without a value.
X`009.i -4;#2##As above, but supress "block compression"
X`009.i -4;#3##Supress block compression and do not output
X`009a compress header block.  This is for compatiblity
X`009with a quite early version of Unix compress (and requires
X`009conditional-compilation to use).
X`009.lm -4.s
X`009Note that the -B (binary) option is ignored unless
X`009the input file is "record-oriented", such as a terminal
X`009or mailbox.
X`009.lm -8.s
X`009The other two arguments are the input and output
X`009filenames respectively.  Redirection is supported,
X`009however, the output must be a disk/tape file.
X
X`009The file format is almost identical to the current
X`009Unix implementation of compress (V4.0).  Files written
X`009by Unix compress should be readable by lzdcmp.  Files
X`009written by lzcomp in export (-x) format will be
X`009readable by Unix compress (except that lzcomp outputs
X`009two "clear" codes to mark EOF.  A patch to Unix
X`009compress is available.)
X
XVMS Restrictions
X
X`009VMS Private mode stores the true name and attributes
X`009of the input file into the compressed file and lzdcmp
X`009restores the attributes (and filename if requested).
X`009The following restrictions apply -- they may be lifted
X`009in the future as they are primarily due to the author's
X`009lack of understanding of the intricacies of of VMS I/O:
X
X`009    All files must be stored on disk.
X`009    The lzcomp output file must be specified directly.
X
X`009Also, for all usage on VMS, the compressed file must
X`009be written to, and read from disk.
X
XLZW compression algorithm
X
X`009This section is abstracted from Terry Welch's article
X`009referenced below.  The algorithm builds a string
X`009translation table that maps substrings in the input
X`009into fixed-length codes.  The compress algorithm may
X`009be described as follows:
X
X`009  1. Initialize table to contain single-character
X`009     strings.
X`009  2. Read the first character.  Set <w> (the prefix
X`009     string) to that character.
X`009  3. (step): Read next input character, K.
X `009  4. If at end of file, output code(<w>); exit.
X`009  5. If <w>K is in the string table:
X`009`009Set <w> to <w>K; goto step 3.
X`009  6. Else <w>K is not in the string table.
X`009`009Output code(<w>);
X`009`009Put <w>K into the string table;
X`009`009Set <w> to K; Goto step 3.
X
X`009"At each execution of the basic step an acceptable input
X`009string <w> has been parsed off.  The next character K is
X`009read and the extended string <w>K is tested to see if it
X`009exists in the string table.  If it is there, then the
X`009extended string becomes the parsed string <w> and the
X`009step is repeated.  If <w>K is not in the string table,
X`009then it is entered, the code for the successfully
X`009parsed string <w> is put out as comprssed data, the
X`009character K becomes the beginning of the next string,
X`009and the step is repeated."
X
X`009The decompression algorithm translates each received
X`009code into a prefix string and extension [suffix] character.
X`009The extension character is stored (in a push-down stack),
X`009and the prefix translated again, until the prefix is a
X`009single character, which completes decompression of this
X`009code.  The entire code is then output by popping the
X`009stack.
X
X`009"An update to the string table is made for each code received
X`009(except the first one).  When a code has been translated,
X`009its final character is used as the extension character,
X`009combined with the prior string, to add a new string to
X`009the string table.  This new string is assigned a unique
X`009code value, which is the same code that the compressor
X`009assigned to that string.  In this way, the decompressor
X`009incrementally reconstructs the same string table that
X`009the decompressor used.... Unfortunately ... [the algorithm]
X`009does not work for an abnormal case.
X
X`009The abnormal case occurs whenever an input character string
X`009contains the sequence K<w>K<w>K, where K<w> already
X`009appears in the compressor string table."
X
X`009The decompression algorithm, augmented to handle
X`009the abnormal case, is as follows:
X
X`009  1. Read first input code;
X`009     Store in CODE and OLDcode;
X`009     With CODE = code(K), output(K);  FINchar = K;
X`009  2. Read next code to CODE; INcode = CODE;
X`009     If at end of file, exit;
X`009  3. If CODE not in string table (special case) then
X`009`009Output(FINchar);
X`009`009CODE = OLDcode;
X`009`009INcode = code(OLDcode, FINchar);
X
X`009  4. If CODE == code(<w>K) then
X`009`009Push K onto the stack;
X`009`009CODE == code(<w>);
X`009`009Goto 4.
X
X`009  5. If CODE == code(K) then
X`009`009Output K;
X`009`009FINchar = K;
X
X`009  6. While stack not empty
X`009`009Output top of stack;
X`009`009Pop stack;
X
X`009  7. Put OLDcode,K into the string table.
X`009     OLDcode = INcode;
X`009     Goto 2.
X
X`009The algorithm as implemented here introduces two additional
X`009complications.
X
X`009The actual codes are transmitted using a variable-length
X`009encoding.  The lowest-level routines increase the number
X`009of bits in the code when the largest possible code is
X`009transmitted.
X
X`009Periodically, the algorithm checks that compression is
X`009still increasing.  If the ratio of input bytes to output
X`009bytes decreases, the entire process is reset.  This can
X`009happen if the characteristics of the input file change.
X
XVMS Private File Structure
X
X`009In VMS Private mode, the compressed data file contains
X`009a variable-length (but compressed) file header with the
X`009file "attributes" needed by the operating system to
X `009construct the file.  This allows the decompression
X`009program to recreate the file in its original format,
X`009which is essential if ISAM databases are compressed.
X
X`009The overall file format is as follows:
X`009.lm +8
X`009.p -8
X`009LZ_SOH`009"start of header" signal (this value cannot appear
X`009in user data).
X
X`009A variable-length data record (maximum 256 bytes)
X`009containing the header name, followed by whitespace, followed
X`009by header-specific information.  In this case, the name
X`009record will contain the string "vms$attributes" followed
X`009by the number of bytes in the attribute data block.
X`009(I assume that the name record will consist of a facility
X`009name, such as "vms", followed by a dollar sign, followed
X`009by a facility-unique word.)
X`009.p -8
X`009LZ_EOR`009Signals "end of record".
X
X`009This is followed by a VMS file attributes record (generated
X`009by a VMS system library`009routine).
X`009.p -8
X`009LZ_ETX`009Signals "end of segment".
X`009.p -8
X`009ST_STX`009Signals "start of text" (i.e., start of data file).
X
X`009This is followed by the user data file.
X`009.p -8
X`009LZ_ETX`009Signals "end of segment"
X`009.p -8
X`009LZ_ETX`009Two in a row signals "end of file".
X`009.s.lm -8
X`009Note that this format can easily be extended to include
X`009trailer records (with file counts and checksums) and/or
X`009multiple data files in one compressed file.
X
X`009Note also that the LZ_CLEAR code may appear in headers
X`009or data files to cause the decompression program to
X`009"readapt" to the characteristics of the input data.
X`009LZ_STX and LZ_SOH reset the compression algorithm.
X`009LZ_EOR does not.
X
XAuthors
X
X`009The algorithm is from "A Technique for High Performance
X`009Data Compression."  Terry A. Welch. IEEE Computer Vol 17,
X`009No. 6 (June 1984), pp 8-19.
X
X`009This revision is by Martin Minow.
X
X`009Unix Compress authors are as follows:
X`009.s.nf
X`009Spencer W. Thomas`009(decvax!harpo!utah-cs!utah-gr!thomas)
X`009Jim McKie`009`009(decvax!mcvax!jim)
X`009Steve Davies`009`009(decvax!vax135!petsd!peora!srd)
X`009Ken Turkowski`009`009(decvax!decwrl!turtlevax!ken)
X`009James A. Woods`009`009(decvax!ihnp4!ames!jaw)
X`009Joe Orost`009`009(decvax!vax135!petsd!joe)
X`009.s.f
X
XEdit history
X
X`009JAH is James A. Harvey, IUPUI Academic Computing Services
X
X`009Edit`009Who`009Date
X`009IU-1`009JAH`00928-Jun-1988
X`009Don't realloc strings on stack or argv strings in openinput.
X`009This was causing command-line arguments to be overwritten
X`009when linked with VAXC RTL version 2.3-024.
X
X`009IU-2`009JAH`00928-Jun-1988
X`009Use STR$ routines to allocate/deallocate dynamic strings in
X`009lzvioisam (fdl_open, fdl_free).  The old code was causing an
X`009access violation when linked with VAXC RTL version 2.3-024.
X
X`009IU-3`009JAH`00928-Jun-1988
X`009Fix "unknown option" error message to type out the unknown
X`009option rather than the one after it.
X
X`009IU-4`009JAH`00928-Jun-1988
X`009Add a "Usage:" line to the helptext.
X#endif
X
X/*
X * Compatible with compress.c, v3.0 84/11/27
X */
X
X/*)BUILD
X`009`009$(PROGRAM) = lzcomp
X`009`009$(INCLUDE) = lz.h
X`009`009$(CPP) = 1
X`009`009$(FILES) = { lzcmp1.c lzcmp2.c lzcmp3.c lzio.c lzvio.c }
X*/
X`012
X#include`009"lz.h"
X
X
X#ifdef unix
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X/*
X * These global parameters are written to the compressed file.
X * The decompressor needs them.  The initialized values are defaults
X * and are modified by command line arguments.
X */
Xshort`009`009maxbits = BITS;`009`009/* settable max # bits/code`009*/
Xcode_int maxmaxcode = 1 << BITS; `009/* Totally largest code`009*/
Xcode_int`009hsize = HSIZE;`009`009/* Actual hash table size`009*/
X
X/*
X * Flags (command line arguments) to control compression.
X */
X#if VMS_V4
Xflag`009`009export = 0;`009`009/* Assume vms "private" mode`009*/
X#else
Xflag`009`009export = 1;`009`009/* Assume Unix compatible mode`009*/
X#endif
Xflag`009`009block_compress = TRUE;`009/* Assume block compression`009*/
Xflag`009`009binary = FALSE;`009`009/* Reading text file if FALSE`009*/
Xflag`009`009noheader = FALSE;`009/* No magic header if TRUE`009*/
Xflag`009`009verbose = VERBOSE_DEFAULT; /* Non-zero for status/debug`009*/
Xflag`009`009background = FALSE;`009/* TRUE (Unix) if detached`009*/
Xreadonly flag`009is_compress = TRUE;`009/* for lzio.c (needed?)`009`009*/
Xlong`009`009fsize;`009`009`009/* Input file size in bytes`009*/
Xchar`009`009*infilename = NULL;`009/* For error printouts`009`009*/
Xchar`009`009*outfilename = NULL;`009/* For openoutput and errors`009*/
Xint`009`009firstcode;`009`009/* First code after internals`009*/
Xcount_int`009tot_incount = 0;`009/* Total number of input bytes`009*/
Xcount_int`009tot_outcount = 0;`009/* Total number of output codes`009*/
Xextern count_int in_count;
Xextern count_int out_count;
Xstatic long`009start_time;`009`009/* Time we started (in msec)`009*/
Xextern long`009cputime();`009`009/* Returns process time in msec`009*/
XSTREAM`009`009instream;
XSTREAM`009`009outstream;
Xchar_type`009inbuffer[MAXIO];
Xchar_type`009outbuffer[MAXIO];
Xstatic STREAM`009mem_stream;
Xjmp_buf`009`009failure;
X#if VMS_V4
X#include types
X#include stat
X#include descrip
X#ifndef FDLSTUFF
X#define FDLSTUFF char
X#endif
XFDLSTUFF`009*fdl_input;
XFDLSTUFF`009*fdl_output;
Xstatic struct dsc$descriptor fdl_descriptor;
X#endif
X`012
Xmain(argc, argv)
Xint`009`009argc;
Xchar`009`009*argv[];
X/*
X * Compress mainline
X */
X{
X#ifndef`009decus
X`009/*
X`009 * background is TRUE if running detached from the command terminal.
X`009 */
X`009background = (signal(SIGINT, SIG_IGN) == SIG_IGN) ? TRUE : FALSE;
X`009if (!background)
X`009    background = !isatty(fileno(stderr));
X`009if (!background) {
X`009    if (verbose > 0)
X`009`009signal(SIGINT, abort);
X`009    else {
X`009`009signal(SIGINT, interrupt);
X`009`009signal(SIGSEGV, address_error);
X`009    }
X`009}
X#endif
X`009if (setjmp(failure) == 0) {
X`009    setup(argc, argv);`009`009/* Command line parameters`009*/
X`009    openinput();`009`009/* Open input, set instream`009*/
X`009    getfilesize();`009`009/* Get input file size`009`009*/
X`009    gethashsize();`009`009/* Get actual hash table size`009*/
X`009    initialize();`009`009/* Set maxbits and the like`009*/
X`009    openoutput();`009`009/* Open output file`009`009*/
X`009    if (verbose > 0)
X`009`009start_time = cputime();
X`009    put_magic_header();
X`009    init_compress(TRUE);
X`009    compress(&instream);
X#if VMS_V4
X`009    if (export == 0) {
X`009`009outputcode((code_int) LZ_ETX);
X`009`009outputcode((code_int) LZ_ETX);
X`009`009fdl_close(fdl_input);
X`009    }
X`009    else
X#endif
X`009    if (block_compress) {
X`009`009outputcode((code_int) LZ_CLEAR);
X`009`009outputcode((code_int) LZ_CLEAR);
X`009    }
X`009    outputcode((code_int) -1);`009`009/* Flush output buffers`009*/
X#if VMS_V4
X`009    if (export == 0)
X`009`009fdl_close(fdl_output);
X`009    else {
X`009`009fclose(stdout);
X`009    }
X#else
X`009    fclose(stdout);
X#endif
X`009    if (verbose > 0) {
X`009`009start_time = cputime() - start_time;
X`009`009tot_incount += in_count;
X`009`009tot_outcount += out_count;
X`009`009fprintf(stderr, "%ld chars in, %ld bytes out, ",
X`009`009    tot_incount, tot_outcount);
X`009`009if (tot_outcount > 0) {
X`009`009    divout("compression ratio: ",
X`009`009`009(long) tot_incount, (long) tot_outcount, "");
X`009`009    divout(" (",
X`009`009`009((long) tot_incount - (long) tot_outcount) * 100,
X`009`009`009(long) tot_incount, "%)\n");
X`009`009}
X`009`009fprintf(stderr,
X`009`009    "%ld.%02ld seconds (process time) for compression.\n",
X`009`009    start_time / 1000L, (start_time % 1000L) / 10L);
X`009`009if (start_time > 0) {
X`009`009    divout("  ", (long) tot_incount * 10L,
X`009`009`009(start_time + 50L) / 100L,
X`009`009`009" input bytes per second.\n");
X`009`009}
X`009    }
X`009    exit(IO_SUCCESS);
X`009}
X`009else {
X`009    fprintf(stderr, "Error when compressing \"%s\" to \"%s\"\n",
X`009`009(infilename  == NULL) ?
X`009`009    "<input file unknown>" : infilename,
X`009`009(outfilename == NULL) ?
X`009`009    "<output file unknown>" : outfilename);
X`009    if (errno != 0)
X`009`009perror("lzcomp fatal error");
X`009    exit(IO_ERROR);
X`009}
X}
X`012
Xdivout(leader, numer, denom, trailer)
Xchar`009`009*leader;
Xlong`009`009numer;
Xlong`009`009denom;
Xchar`009`009*trailer;
X/*
X * Print numer/denom without floating point on small machines.
X */
X{
X`009fprintf(stderr, "%s%ld.%02ld%s",
X`009    leader, numer / denom, ((numer % denom) * 100L) / denom, trailer);
X}
X
Xstatic
Xinitialize()
X/*
X * Mung some global values.
X */
X{
X`009if (maxbits < INIT_BITS)`009/* maxbits is set by the -M `009*/
X`009    maxbits = INIT_BITS;`009/* option.  Make sure it's`009*/
X`009if (maxbits > BITS)`009`009/* within a reasonable range`009*/
X`009    maxbits = BITS;
X`009maxmaxcode = 1 << maxbits;`009/* Truly biggest code`009`009*/
X`009if (export == 0)
X`009    firstcode = LZ_FIRST;`009/* VMS private`009`009`009*/
X`009else if (block_compress)
X`009    firstcode = LZ_CLEAR + 1;`009/* Default`009`009`009*/
X`009else
X`009    firstcode = 256;`009`009/* Backwards compatible`009`009*/
X}
X`012
Xput_magic_header()
X/*
X * Write the magic header bits.
X */
X{
X#ifndef COMPATIBLE
X`009if (export && !noheader) {
X`009    PUT(HEAD1_MAGIC, &outstream);
X`009    PUT(HEAD2_MAGIC, &outstream);
X`009    PUT(maxbits | ((block_compress) ? BLOCK_MASK : 0),
X`009`009&outstream);
X`009}
X#if VMS_V4
X`009else if (export == 0) {
X`009    char`009`009text[256];
X`009    /*
X`009     * VMS private mode (with attribute block)
X`009     */
X`009    PUT(HEAD1_MAGIC, &outstream);
X`009    PUT(VMS_HEAD2_MAGIC, &outstream);
X`009    PUT((char) (maxbits | BLOCK_MASK), &outstream);
X`009    PUT(firstcode - 0x100, &outstream);
X`009    init_compress();
X`009    outputcode(LZ_SOH);
X#if DEBUG
X`009    if (strlen(ATT_NAME) != ATT_SIZE) {
X`009`009fprintf("\"%s\", expected %d, got %d\n",
X`009`009    ATT_NAME, ATT_SIZE, strlen(ATT_NAME));
X`009    }
X#endif
X`009    sprintf(text, "%s%d;", ATT_NAME, fdl_descriptor.dsc$w_length);
X`009    mem_compress(text, strlen(text));
X`009    outputcode(LZ_EOR);
X`009    mem_compress(fdl_descriptor.dsc$a_pointer,
X`009`009`009 fdl_descriptor.dsc$w_length);
X`009    fdl_free(&fdl_descriptor);
X`009    outputcode(LZ_ETX);
X`009    outputcode(LZ_STX);
X`009}
X#endif
X#endif
X}
X
Xmem_compress(datum, length)
Xchar_type`009*datum;
Xint`009`009length;
X/*
X * Compress from memory
X */
X{
X`009mem_stream.bp = mem_stream.bstart = datum;
X`009mem_stream.bsize = length;
X`009mem_stream.bend = datum + length;
X`009mem_stream.func = lz_eof;
X`009compress(&mem_stream);
X}
X`012
X/*
X * This routine is used to tune the hash table size according to
X * the file size.  If the filesize is unknown, fsize should be
X * set to zero.
X */
X
Xtypedef struct TUNETAB {
X    long`009fsize;
X    code_int`009hsize;
X} TUNETAB;
X
Xstatic readonly TUNETAB tunetab[] = {
X#if HSIZE > 5003
X    {`0091 << 12,`009 5003`009},
X#endif
X#if HSIZE > 9001
X    {`0091 << 13,`009 9001`009},
X#endif
X#if HSIZE > 18013
X    {`0091 << 14,`00918013`009},
X#endif
X#if HSIZE > 35023
X    {`0091 << 15,`00935023`009},
X    {`009  47000,`00950021`009},
X#endif
X    {`009      0,`009    0`009},
X};
X
Xstatic
Xgethashsize()
X/*
X * Tune the hash table parameters for small files.
X * We don't have a good way to find the file size on vms V3.
X * fsize is set to zero if we can't find it.
X */
X{
X`009register TUNETAB`009*tunep;
X
X`009hsize = HSIZE;
X`009if (fsize > 0) {
X`009    for (tunep = tunetab; tunep->fsize != 0; tunep++) {
X`009`009if (fsize < tunep->fsize) {
X`009`009    hsize = tunep->hsize;
X`009`009    break;
X`009`009}
X`009    }
X`009}
X}
X`012
Xstatic
Xgetfilesize()
X/*
X * Set fsize to the input filesize (in bytes) if possible.
X * Magic for all operating systems.
X */
X{
X#ifdef`009rsx
X`009extern char`009f_efbk;`009/* F.EFBK -- highest block in file`009*/
X#define`009fdb(p,offset)`009(stdin->io_fdb[((int) &p + offset)] & 0xFF)
X#define efbk(offset)`009fdb(f_efbk, offset)
X`009extern char`009f_rtyp;`009/* F.RTYP -- Record type`009`009*/
X`009extern char`009f_ratt;`009/* F.RATT -- Record attributes`009`009*/
X`009/*
X`009 * Note: Block number is stored high-order word first.
X`009 */
X`009fsize = efbk(2)
X`009    + (efbk(3) << 8)
X`009    + (efbk(0) << 16)
X`009    + (efbk(1) << 24);
X`009fsize *= 512;
X#endif
X#ifdef`009rt11
X`009fsize = stdin->io_size;`009`009/* Set by Decus C`009`009*/
X`009fsize *= 512;
X#endif
X#ifdef`009vms
X#if VMS_V4
X`009struct stat`009statbuf;
X
X`009fsize = 0;
X`009if (export != 0) {
X`009    if (fstat(fileno(stdin), &statbuf) == 0)
X`009`009fsize = (long) statbuf.st_size;
X`009}
X`009else {
X`009    fsize = (long) fdl_fsize(fdl_input);
X`009}
X#else
X`009fsize = 0;`009`009`009`009/* Can't find filesize`009*/
X#endif
X#endif
X#ifdef`009unix
X`009struct stat`009statbuf;
X
X`009fsize = 0;
X`009if (fstat(fileno(stdin), &statbuf) == 0)
X`009    fsize = (long) statbuf.st_size;
X#endif
X}
X`012
Xstatic readonly char *helptext[] = {
X`009"Usage: lzcomp [-options] [infile [outfile]]",`009`009/*[IU-2]*/
X`009"The following options are valid:",
X`009"-B\tBinary file (important on VMS/RSX, ignored on Unix)",
X`009"-M val\tExplicitly set the maximum number of code bits",
X`009"-V val\tPrint status information (or debugging) to stderr",
X`009"-X val\tSet export (compatiblity) mode:",
X#if VMS_V4
X`009"  -X 0\tExplicitly choose VMS Private mode",
X#endif
X`009"  -X 1\t(default if -X specified, output format is compatible",
X`009      "\twith Unix compress V3.0",
X`009"  -X 2\tCompatible with Unix compress 3.0, block compression",
X`009      "\tsupressed.",
X#ifdef COMPATIBLE
X`009"  -X 3No header (file is readable by old compress)",
X#endif
X`009NULL,
X};
X
Xstatic
Xsetup(argc, argv)
Xint`009`009argc;
Xchar`009`009*argv[];
X/*
X * Get parameters and open files.  Exit fatally on errors.
X */
X{
X`009register char`009*ap;
X`009register int`009c;
X`009char`009`009**hp;
X`009auto int`009i;
X`009int`009`009j;
X
X#ifdef`009vms
X`009argc = getredirection(argc, argv);
X#endif
X`009for (i = j = 1; i < argc; i++) {
X`009    ap = argv[i];
X`009    if (*ap++ != '-' || *ap == EOS)`009/* Filename?`009`009*/
X`009`009argv[j++] = argv[i];`009`009/* Just copy it`009`009*/
X`009    else {
X`009`009while ((c = *ap++) != EOS) {
X`009`009    if (islower(c))
X`009`009`009c = toupper(c);
X`009`009    switch (c) {
X`009`009    case 'B':
X`009`009`009binary = TRUE;
X`009`009`009break;
X
X`009`009    case 'M':
X`009`009`009maxbits = getvalue(ap, &i, argv);
X`009`009`009if (maxbits < MIN_BITS) {
X`009`009`009    fprintf(stderr, "Illegal -M value\n");
X`009`009`009    goto usage;
X`009`009`009}
X`009`009`009break;
X
X`009`009    case 'V':
X`009`009`009verbose = getvalue(ap, &i, argv);
X`009`009`009break;
X
X`009`009    case 'X':
X`009`009`009export = getvalue(ap, &i, argv);
X`009`009`009if (export < 0 || export > 3) {
X`009`009`009    fprintf(stderr, "Illegal -X value: %d\n", export);
X`009`009`009    goto usage;
X`009`009`009}
X`009`009`009block_compress = "\1\1\0\0"[export];
X`009`009`009noheader       = "\0\0\0\1"[export];
X`009`009`009export         = "\0\1\1\1"[export];
X`009`009`009break;
X
X`009`009    default:
X`009`009`009ap--;`009`009`009`009`009/*[IU-3]*/
X`009`009`009fprintf(stderr, "Unknown option '%c' in \"%s\"\n",
X`009`009`009`009*ap, argv[i]);
Xusage:`009`009`009for (hp = helptext; *hp != NULL; hp++)
X`009`009`009    fprintf(stderr, "%s\n", *hp);
X`009`009`009FAIL("usage");
X`009`009    }`009`009`009`009/* Switch on options`009*/
X`009`009}`009`009`009`009/* Everything for -xxx`009*/
X`009    }`009`009`009`009`009/* If -option`009`009*/
X`009}`009`009`009`009`009/* For all argc's`009*/
X`009/*  infilename = NULL; */`009`009/* Set "stdin"  signal`009*/
X`009/* outfilename = NULL; */`009`009/* Set "stdout" signal`009*/
X`009switch (j) {`009`009`009`009/* Any file arguments?`009*/
X`009case 3:`009`009`009`009`009/* both files given`009*/
X`009    if (!streq(argv[2], "-"))`009`009/* But - means stdout`009*/
X`009`009outfilename = argv[2];
X`009case 2:`009`009`009`009`009/* Input file given`009*/
X`009    if (!streq(argv[1], "-")) {
X`009`009infilename = argv[1];
X`009    }
X`009    break;
X
X`009case 0:`009`009`009`009`009/* None!`009`009*/
X`009case 1:`009`009`009`009`009/* No file arguments`009*/
X`009    break;
X
X`009default:
X`009    fprintf(stderr, "Too many file arguments\n");
X`009    FAIL("too many files");
X`009}
X}
X`012
Xstatic int
Xgetvalue(ap, ip, argv)
Xregister char`009`009*ap;
Xint`009`009`009*ip;
Xchar`009`009`009*argv[];
X/*
X * Compile a "value".  We are currently scanning *ap, part of argv[*ip].
X * The following are possible:
X *`009-x123`009`009return (123) and set *ap to EOS so the caller
X *`009ap^`009`009cycles to the next argument.
X *
X *`009-x 123`009`009*ap == EOS and argv[*ip + 1][0] is a digit.
X *`009`009`009return (123) and increment *i to skip over the
X *`009`009`009next argument.
X *
X *`009-xy or -x y`009return(1), don't touch *ap or *ip.
X *
X * Note that the default for "flag option without value" is 1.  This
X * can only cause a problem for the -M option where the value is
X * mandatory.  However, the result of 1 is illegal as it is less
X * than INIT_BITS.
X */
X{
X`009register int`009result;
X`009register int`009i;
X
X`009i = *ip + 1;
X`009if (isdigit(*ap)) {
X`009    result = atoi(ap);
X`009    *ap = EOS;
X`009}
X`009else if (*ap == EOS
X`009      && argv[i] != NULL
X`009      && isdigit(argv[i][0])) {
X`009    result = atoi(argv[i]);
X`009    *ip = i;
X`009}
X`009else {
X`009    result = 1;
X`009}
X`009return (result);
X}
X`012
Xopeninput()
X{
X#ifdef decus
X`009if (infilename == NULL) {
X`009    infilename = malloc(256 + 1);
X`009    fgetname(stdin, infilename);
X`009    infilename = realloc(infilename, strlen(infilename) + 1);
X`009}
X`009else {
X`009    if (freopen(infilename, (binary) ? "rn" : "r", stdin) == NULL) {
X`009`009perror(infilename);
X`009`009FAIL("can't reopen input");
X`009    }
X`009}
X#else
X#ifdef vms
X#if VMS_V4
X`009if (export == 0) {
X`009    char`009`009*fname;
X/*[IU-1]    char`009`009filename[256];`009*/
X
X/*[IU-1]    if ((fname = infilename) == NULL) {
X`009`009fgetname(stdin, filename);
X`009`009fname = filename;
X`009    }
X[IU-1]*/
X`009    fname = malloc(256 + 1);`009`009/*[IU-1]*/
X`009    if (infilename == NULL)`009`009/*[IU-1]*/
X`009`009fgetname(stdin, fname);`009`009/*[IU-1]*/
X`009    else    `009`009`009`009/*[IU-1]*/
X`009`009strcpy(fname, infilename);`009/*[IU-1]*/
X
X`009    if ((fdl_input = fdl_open(fname, &fdl_descriptor)) == NULL) {
X`009`009if ((fdl_status & 01) == 0) {
X`009`009    fdl_message(NULL, fname);
X`009`009    FAIL("can't fdl_open");
X`009`009}
X`009`009fprintf(stderr,
X`009`009    "Cannot open \"%s\" in vms private format,", fname);
X`009`009fprintf(stderr, " trying export format.\n");
X`009`009export = TRUE;
X`009`009goto try_export;
X`009    }
X`009    fclose(stdin);
X`009    stdin = NULL;
X/*[IU-2]    infilename = malloc(256 + 1);`009*/
X`009    infilename = realloc(fname, strlen(fname) + 1);
X`009    if (verbose > 1) {
X/*[IU-2]`009fprintf(stderr, "FDL information for \"%s\"\n", filename); */
X        `009fprintf(stderr, "FDL information for \"%s\"\n", infilename);
X`009`009fdl_dump(&fdl_descriptor, stderr);
X`009    }
X`009    goto opened;
X`009}
Xtry_export:
X#endif
X`009if (infilename == NULL) {
X`009    infilename = malloc(256 + 1);
X`009    fgetname(stdin, infilename);
X`009    infilename = realloc(infilename, strlen(infilename) + 1);
X`009}
X`009else {
X#if VMS_V4
X`009    if ((stdin = freopen(infilename, "r", stdin)) == NULL) {
X#else
X`009    if (freopen(infilename, "r", stdin) == NULL) {
X#endif
X`009`009perror(infilename);
X`009`009exit(IO_ERROR);
X`009    }
X`009}
X#else
X`009if (infilename == NULL)
X`009    infilename = "stdin";
X`009else {
X`009    if (freopen(infilename, "r", stdin) == NULL) {
X`009`009perror(infilename);
X`009`009exit(IO_ERROR);
X`009    }
X`009}
X#endif
X#endif
Xopened:`009instream.bp = instream.bend = NULL;
X`009instream.bstart = inbuffer;
X`009instream.bsize = sizeof inbuffer;
X`009instream.func = lz_fill;
X}
X`012
Xopenoutput()
X/*
X * Open the output file (after the input file has been opened).
X * if outfilename == NULL, it's already open on stdout.
X */
X{
X`009if (outfilename == NULL) {
X#if VMS_V4
X#if 0`009`009`009`009`009/* The following doesn't work`009*/
X`009    outfilename = malloc(256 + 1);
X`009    fgetname(stdout, outfilename);
X`009    outfilename = realloc(outfilename, strlen(outfilename) + 1);
X`009    if (export == 0) {
X`009`009fclose(stdout);
X`009`009stdout = NULL;`009`009/* Can't do terminal test below`009*/
X`009`009if ((fdl_output = fdl_create(NULL, outfilename)) == NULL) {
X`009`009    if ((fdl_status & 01) == 0)
X`009`009`009fdl_message(NULL, outfilename);
X`009`009    fprintf(stderr, "Can't create \"%s\"\n", outfilename);
X`009`009    FAIL("can't fdl_create");
X`009`009}
X`009    }
X#else
X`009    fprintf(stderr,
X`009`009"Restriction: The output file must be specified.\n");
X`009    FAIL("can't redirect on VMS V4");
X#endif
X#else
X#ifdef`009vms
X`009    outfilename = malloc(256 + 1);
X`009    fgetname(stdout, outfilename);
X`009    outfilename = realloc(outfilename, strlen(outfilename) + 1);
X#else
X#ifdef decus
X`009    outfilename = malloc(256 + 1);
X`009    fgetname(stdout, outfilename);
X`009    outfilename = realloc(outfilename, strlen(outfilename) + 1);
X#else
X`009    outfilename = "<stdout>";
X#endif
X#endif
X#endif
X`009}
X`009else {
X#if VMS_V4
X`009    if (export == 0) {
X`009`009fclose(stdout);
X`009`009stdout = NULL;`009`009/* Can't do terminal test below`009*/
X`009`009if ((fdl_output = fdl_create(NULL, outfilename)) == NULL) {
X`009`009    if ((fdl_status & 01) == 0)
X`009`009`009fdl_message(NULL, outfilename);
X`009`009    fprintf(stderr,
X`009`009`009"Can't create \"%s\" (VMS private)\n", outfilename);
X`009`009    FAIL("can't fdl_create");
X`009`009}
X`009    }
X`009    else {
X`009`009if (freopen(outfilename, "w", stdout) == NULL) {
X`009`009    perror(outfilename);
X`009`009    FAIL("can't create");
X`009`009}
X`009    }
X#else
X#ifdef decus
X`009    if (freopen(outfilename, "wn", stdout) == NULL) {
X`009`009perror(outfilename);
X`009`009FAIL("can't create");
X`009    }
X#else
X`009    if (freopen(outfilename, "w", stdout) == NULL) {
X`009`009perror(outfilename);
X`009`009FAIL("can't create");
X`009    }
X#endif
X#endif
X`009}
X`009if (stdout != NULL && isatty(fileno(stdout))) {
X`009    fprintf(stderr, "%s: is a terminal.  We object.\n",
X`009`009outfilename);
X`009    FAIL("can't create");
X`009}
X`009outstream.bp = outstream.bstart = outbuffer;
X`009outstream.bend = outbuffer + sizeof outbuffer;
X`009outstream.bsize = sizeof outbuffer;
X`009outstream.func = lz_flush;
X}
$ GoSub Convert_File
$ Goto Part3

