/*
	       unsit - Macintosh StuffIt file extractor

		     Version 1.5c, for StuffIt 1.5

			    August 3, 1989

This program will unpack a Macintosh StuffIt file into separate files.
The data fork of a StuffIt file contains both the data and resource
forks of the packed files.  The program will unpack each Mac file into
separate .data, .rsrc., and .info files that can be downloaded to a
Mac using macput.  The program is much like the "unpit" program for
breaking apart Packit archive files.

			***** IMPORTANT *****
To extract StuffIt files that have been compressed with the Lempel-Ziv
compression method, unsit pipes the data through the "compress"
program with the appropriate switches, rather than incorporate the
uncompression routines within "unsit".  Therefore, it is necessary to
have the "compress" program on the system and in the search path to
make "unsit" work.  "Compress" is available from the comp.sources.unix
archives.

The program syntax is much like unpit and macput/macget, with some added
options:

	unsit [-rdulvqfm] stuffit-file.data

The -r and -d flags will cause only the resource and data forks to be
written.  The -u flag will cause only the data fork to be written and
to have carriage return characters changed to Unix newline characters.
The -l flag will make the program only list the files in the StuffIt
file.  The -v flag causes the program to list the names, sizes, type,
and creators of the files it is writing.  The -q flag causes it to
list the name, type and size of each file and wait for a 'y' or 'n'
for either writing that file or skipping it, respectively.  The -m
flag is used when the input file in in the MacBinary format instead of
three separate .data, .info, and .rsrc files.  It causes the program
to skip the 128 byte MacBinary header before looking for the StuffIt
header.

Version 1.5 of the unsit supports extracting files and folders as
implemented by StuffIt 1.5's "Hierarchy Maintained Folder" feature.
Each folder is extracted as a subdirectory on the Unix system with the
files in the folder placed in the corresponding subdirectory.  The -f
option can be used to "flatten" out the hierarchy and unsit will store
all the files in the current directory.  If the query option (-q) is
used and a "n" response is given to a folder name, none of the files
or folders in that folder will be extraced.

Some of the program is borrowed from the macput.c/macget.c programs.
Many, many thanks to Raymond Lau, the author of StuffIt, for including
information on the format of the StuffIt archives in the
documentation.  Several changes and enhancements supplied by David
Shanks (cde@atelabs.UUCP) have been incorporated into the program for
doing things like supporting System V and recognizing MacBinary files.
I'm always glad to receive advice, suggestions, or comments about the
program so feel free to send whatever you think would be helpful


	Author: Allan G. Weber
		weber%brand.usc.edu@oberon.usc.edu
		...sdcrdcf!usc-oberon!brand!weber
	Date:   April 3, 1989

                              - - - - - - - - - -

                        Amiga port (Saturday 10-Aug-91)
                              mods by Anthon Pang

     1) '-u' option not supported (converting CR's to LF's in text files)
     2) prints compression id when unknown (during extract)
     3) '-l' option -- changed format of listings
     4) ".sit" extension is optional for "filename.sit", unless file without
        extension also exists (ie user should specify ".sit" explicitly)
     5) included version 5.3 of "compress"...ported by Matt Dillon for uucp
     6) changed extensions: ".data" -> ".d", ".rsrc" -> ".r", ".info" -> ".i"
        (mainly because ".info" is already in use on the Amiga for icons)
     7) some ANSI-cizing and reformatting

                     Addendum to Amiga Port (Sept 8, 1991)

     8) Corrected code for uncompressed lzw files, with spaces in names.
     9) Using popen, pclose emulation package.
*/

#include <stdio.h>

#ifdef AMIGA
#define SHORTNAMES
#include <exec/types.h>
#include <stat.h>
#include <libraries/dosextens.h>
#include "pio.h"
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include "unstuffit.h"

/*
 * The following defines the name of the compress program that is used for the
 * uncompression of Lempel-Ziv compressed files.  If the path is set up to
 * include the right directory, this should work.
 */
#define COMPRESS  "compress"

#define IOBUFSIZ  4096

#define MACBINHDRSIZE  128L

#define INIT_CRC  0L

#define INFOBYTES  128

#ifndef AMIGA
#define BYTEMASK 0xff
#endif

#define S_SIGNATURE    0
#define S_NUMFILES     4
#define S_ARCLENGTH    6
#define S_SIGNATURE2  10
#define S_VERSION     14
#define SITHDRSIZE    22

#define F_COMPRMETHOD    0
#define F_COMPDMETHOD    1
#define F_FNAME          2
#define F_FTYPE         66
#define F_CREATOR       70
#define F_FNDRFLAGS     74
#define F_CREATIONDATE  76
#define F_MODDATE       80
#define F_RSRCLENGTH    84
#define F_DATALENGTH    88
#define F_COMPRLENGTH   92
#define F_COMPDLENGTH   96
#define F_RSRCCRC      100
#define F_DATACRC      102
#define F_HDRCRC       110
#define FILEHDRSIZE    112

#define F_NAMELEN  63
#ifdef SHORTNAMES
#ifdef AMIGA
#define I_NAMELEN  31 /* 30 char file names + '\0' terminator */
#else
#define I_NAMELEN  15 /* 14 char file names + '\0' terminator */
#endif
#else
#define I_NAMELEN  69 /* 63 + strlen (".info") + 1 */
#endif

/* The following are copied out of macput.c/macget.c */
#define I_NAMEOFF  1
/* 65 <-> 80 is the FInfo structure */
#define I_TYPEOFF  65
#define I_AUTHOFF  69
#define I_FLAGOFF  73
#define I_LOCKOFF  81
#define I_DLENOFF  83
#define I_RLENOFF  87
#define I_CTIMOFF  91
#define I_MTIMOFF  95

#define INITED_BUG
#define INITED_OFF  I_FLAGOFF /* offset to byte with Inited flag */
#define INITED_MASK  (~1)     /* mask to '&' with byte to reset it */

#define TEXT 0
#define DATA 1
#define RSRC 2
#define FULL 3
#define DUMP 4

#define NODECODE 0
#define DECODE   1

#define H_ERROR -1
#define H_EOF    0
#define H_WRITE  1
#define H_SKIP   2

struct node {
    int         flag, byte;
    struct node *one, *zero;
  } nodelist[512], *nodeptr, *read_tree(); /* 512 should be big enough */

struct sitHdr sithdr;

/* filenames */
char f_info[I_NAMELEN];
char f_data[I_NAMELEN];
char f_rsrc[I_NAMELEN];

char info[INFOBYTES];
char mname[F_NAMELEN+1];
char uname[F_NAMELEN+1];
char iobuf[IOBUFSIZ];

static char *CompStr[] = {
     "NOT", "RLE", "LZW", "HUF", "???"
};

int mode, txtmode, listonly, verbose, query, flatten;
int bit, chkcrc, numfiles, depth;
FILE *infp;


/* function prototypes */
extern unsigned short updcrc(unsigned short, unsigned char *, int);
extern int getopt(int argc, char *argv[], char *optstring);
void usage();
int extract(char *parent, int skip);
int extractfile(struct fileHdr *fh, int skip);
int readsithdr(struct sitHdr *s);
int readfilehdr(struct fileHdr *f, int skip);
check_access(char *fname);
unsigned short write_file(char *, long, long, unsigned char);
void outc(char *p, int n, FILE *fp);
long get4(char *bp);
short get2(char *bp);
void copy(char *p1, char *p2, int n);
struct node *read_tree();
int getbit();
int gethuffbyte(int decode);


void main(int argc, char *argv[]) {
    extern int optind;
    extern char *optarg;

    int status;
    int c;
    int errflg;
    int macbin;
    char temp[256];

    mode = FULL;
    errflg = 0;
    macbin = 0;
    flatten = 0;
    numfiles = 0;
    depth = 0;

    while ((c = getopt(argc, argv, "dflmqruvx")) != EOF)
      switch (c) {
        case 'r':
          mode = RSRC;
          break;
        case 'd':
          mode = DATA;
          break;
        case 'u':
          mode = TEXT;
          break;
        case 'l':
          listonly++;
          break;
        case 'q':
          query++;
          break;
        case 'v':
          verbose++;
          break;
        case 'x':        /* undocumented */
          mode = DUMP;
          break;
        case 'm':
          macbin = 1;
          break;
        case 'f':
          flatten = 1;
          break;
        case '?':
          errflg++;
          break;
      }

    if (errflg) {
      usage();
      exit(1);
    }

    if (optind == argc) {
      usage();
      exit(1);
    } else {
      if ((infp = fopen(argv[optind], "r")) == NULL) {
        sprintf(temp, "%s.sit", argv[optind]);
        if ((infp = fopen(temp, "r")) == NULL) {        
           fprintf(stderr,"Can't open input file \"%s\"\n",argv[optind]);
           exit(1);
        }
      }
    }

    if (macbin) {
      if (fseek(infp, MACBINHDRSIZE, 0) == -1) {
        fprintf(stderr, "Can't skip over MacBinary header\n");
        exit(1);
      }
    }

    if (readsithdr(&sithdr) == 0) {
      fprintf(stderr, "Can't read file header\n");
      exit(1);
    }
    if (listonly) {
      printf("---------- Data Fork ----------- --------- Resource Fork -------- --------\n"
             "Original Packed Ratio Type  CRC  Original Packed Ratio Type  CRC  Name\n"
             "-------- ------  ---  ----- ---- -------- ------  ---  ----- ---- --------\n");

    }
    /* 
    printf("numfiles=%d, arclength=%ld\n", sithdr.numFiles, sithdr.arcLength);
    */
 
    status = extract("", 0);
    exit((status < 0) ? 1 : 0);
}

void usage() {
     fprintf(stderr, "Usage: unsit [-rdulvqmf] filename\n");
}

/*
  extract(parent, skip) - Extract all files from the current folder.
  char *parent;           name of parent folder
  int  skip;              1 to skip all files and folders in this one
                          0 to extract them

  returns 1 if came an endFolder record
          0 if EOF
  -1 if error (bad fileHdr, bad file, etc.)
*/

int extract(char *parent, int skip) {
    struct fileHdr filehdr;
    struct stat sbuf;
    int status, rstat, sstat, skipit;
    char name[256];

    while (1) {
      rstat = readfilehdr(&filehdr, skip);
      if (rstat == H_ERROR || rstat == H_EOF) {
        status = rstat;
        break;
      }
 /*
 printf("compr=%d, compd=%d, rsrclen=%ld, datalen=%ld, rsrccrc=%d, datacrc=%d\n",
        filehdr.compRMethod, filehdr.compDMethod,
        filehdr.compRLength, filehdr.compDLength,
        filehdr.rsrcCRC, filehdr.dataCRC);
 */

      skipit = (rstat == H_SKIP) ? 1 : 0;

      if (filehdr.compRMethod == endFolder && 
        filehdr.compDMethod == endFolder) {
        status = 1;  /* finished with this folder */
        break;
      } else if (filehdr.compRMethod == startFolder && 
                 filehdr.compDMethod == startFolder) {
        if (!listonly && rstat == H_WRITE && !flatten) {
          sstat = stat(uname, &sbuf);
          if (sstat == -1) { /* directory doesn't exist */
            if (mkdir(uname, 0777) == -1) {
              fprintf(stderr, "Can't create subdirectory %s\n", uname);
              return(-1);
            }
          } else {  /* something exists with this name */
            fprintf(stderr, "Directory name %s already in use\n", uname);
            return(-1);
          }
          if (chdir(uname) == -1) {
            fprintf(stderr, "Can't chdir to %s\n", uname);
            return(-1);
          }
          sprintf(name,"%s:%s", parent, uname);
        }
        depth++;
        status = extract(name, skipit);
        depth--;
        if (status != 1)
          break;  /* problem with folder */
        if (depth == 0) /* count how many top-level files done */
          numfiles++;
        if (!flatten)
#ifdef AMIGA
          chdir("/");
#else
          chdir("..");
#endif
      } else {
        if ((status = extractfile(&filehdr, skipit)) != 1)
          break;
        if (depth == 0) /* count how many top-level files done */
          numfiles++;
      }
      if (numfiles == sithdr.numFiles)
        break;
    }
    return(status);
}

int extractfile(struct fileHdr *fh, int skip) {
    unsigned short crc;
    FILE *fp;

    f_data[0] = f_rsrc[0] = f_info[0] = '\0'; /* assume no output files */
    /* figure out what file names to use and what to do */
    if (!listonly && !skip) {
      switch (mode) {
        case FULL:  /* do both rsrc and data forks */
          sprintf(f_data, "%.*s.d", I_NAMELEN - 2, uname);
          sprintf(f_rsrc, "%.*s.r", I_NAMELEN - 2, uname);
          sprintf(f_info, "%.*s.i", I_NAMELEN - 2, uname);
          break;
        case RSRC:  /* rsrc fork only */
          sprintf(f_rsrc, "%.*s.r", I_NAMELEN - 2, uname);
          break;
        case DATA:  /* data fork only */
        case TEXT:
          sprintf(f_data, "%.*s", I_NAMELEN - 1, uname);
          break;
        case DUMP:  /* for debugging, dump data as is */
          sprintf(f_data, "%.*s.ddump", I_NAMELEN - 7, uname);
          sprintf(f_rsrc, "%.*s.rdump", I_NAMELEN - 7, uname);
          fh->compRMethod = fh->compDMethod = noComp;
          break;
      }
    }

    if (f_info[0] != '\0' && check_access(f_info) != -1) {
      fp = fopen(f_info, "w");
      if (fp == NULL) {
        perror(f_info);
        exit(1);
      }
      fwrite(info, 1, INFOBYTES, fp);
      fclose(fp);
    }

    if (f_rsrc[0] != '\0') {
      txtmode = 0;
      crc = write_file(f_rsrc, fh->compRLength, fh->rsrcLength, fh->compRMethod);
      if (chkcrc && fh->rsrcCRC != crc) {
        fprintf(stderr, "CRC error on resource fork: need 0x%04x, got 0x%04x\n", fh->rsrcCRC, crc);
        return(-1);
      }
    } else {
      fseek(infp, (long) fh->compRLength, 1);
    }

    if (f_data[0] != '\0') {
      txtmode = (mode == TEXT);
      crc = write_file(f_data, fh->compDLength, fh->dataLength, fh->compDMethod);
      if (chkcrc && fh->dataCRC != crc) {
        fprintf(stderr, "CRC error on data fork: need 0x%04x, got 0x%04x\n", fh->dataCRC, crc);
        return(-1);
      }
    } else {
      fseek(infp, (long) fh->compDLength, 1);
    }
    return(1);
}

int readsithdr(struct sitHdr *s) {
    char temp[FILEHDRSIZE];
    int count = 0;

    for (;;) {
      if (fread(temp, 1, SITHDRSIZE, infp) != SITHDRSIZE) {
        fprintf(stderr, "Can't read file header\n");
        return(0);
      }
     
      if (strncmp(temp + S_SIGNATURE,  "SIT!", 4) == 0 &&
               strncmp(temp + S_SIGNATURE2, "rLau", 4) == 0) {
        s->numFiles = get2(temp + S_NUMFILES);
        s->arcLength = get4(temp + S_ARCLENGTH);
        return(1);
      }
    
      if (++count == 2) {
        fprintf(stderr, "Not a StuffIt file\n");
        return(0);
      }
 
      if (fread(&temp[SITHDRSIZE], 1, FILEHDRSIZE - SITHDRSIZE, infp) !=
                                                FILEHDRSIZE - SITHDRSIZE) {
        fprintf(stderr, "Can't read file header\n");
        return(0);
      }
    
      if (strncmp(temp + I_TYPEOFF, "SIT!", 4) == 0 &&
          strncmp(temp + I_AUTHOFF, "SIT!", 4) == 0) { /* MacBinary format */
        fseek(infp, (long)(INFOBYTES-FILEHDRSIZE), 1); /* Skip over header */
      }
   }
}

/*
  readfilehdr - reads the file header for each file and the folder start
  and end records.

  returns: H_ERROR = error
    H_EOF   = EOF
    H_WRITE = write file/folder
    H_SKIP  = skip file/folder
*/

int readfilehdr(struct fileHdr *f, int skip) {
    unsigned short crc;
    int i, n, write_it, isfolder;
    char hdr[FILEHDRSIZE];
    char ch, *mp, *up;
    char *tp, temp[10];

    for (i = 0; i < INFOBYTES; i++)
      info[i] = '\0';

    /* read in the next file header, which could be folder start/end record */
    n = fread(hdr, 1, FILEHDRSIZE, infp);
    if (n == 0)   /* return 0 on EOF */
      return(H_EOF);
    else if (n != FILEHDRSIZE) {
      fprintf(stderr, "Can't read file header\n");
      return(H_ERROR);
    }

    /* check the CRC for the file header */
    crc = INIT_CRC;
    crc = updcrc(crc, (unsigned char *)hdr, FILEHDRSIZE - 2L);
    f->hdrCRC = get2(hdr + F_HDRCRC);
    if (f->hdrCRC != crc) {
      fprintf(stderr, "Header CRC mismatch: got 0x%04x, need 0x%04x\n", f->hdrCRC, crc);
      return(H_ERROR);
    }

    /* grab the name of the file or folder */
    n = hdr[F_FNAME] & BYTEMASK;
    if (n > F_NAMELEN)
      n = F_NAMELEN;
    info[I_NAMEOFF] = n;
    copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n);
    strncpy(mname, hdr + F_FNAME + 1, n);
    mname[n] = '\0';
    /* copy to a string with no illegal Unix characters in the file name */
    mp = mname;
    up = uname;
    while ((ch = *mp++) != '\0') {
      if (ch <= ' ' || ch > '~' || index("/!()[]*<>?\\\"$\';&`", ch) != NULL)
        ch = '_';
      *up++ = ch;
    }
    *up = '\0';

    /* get lots of other stuff from the header */
    f->compRMethod = hdr[F_COMPRMETHOD];
    f->compDMethod = hdr[F_COMPDMETHOD];
    f->rsrcLength = get4(hdr + F_RSRCLENGTH);
    f->dataLength = get4(hdr + F_DATALENGTH);
    f->compRLength = get4(hdr + F_COMPRLENGTH);
    f->compDLength = get4(hdr + F_COMPDLENGTH);
    f->rsrcCRC = get2(hdr + F_RSRCCRC);
    f->dataCRC = get2(hdr + F_DATACRC);

    /* if it's an end folder record, don't need to do any more */
    if (f->compRMethod == endFolder && f->compDMethod == endFolder)
      return(H_WRITE);

    /* prepare an info file in case its needed */

    copy(info + I_TYPEOFF, hdr + F_FTYPE, 4);
    copy(info + I_AUTHOFF, hdr + F_CREATOR, 4);
    copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2);
#ifdef INITED_BUG
    info[INITED_OFF] &= INITED_MASK; /* reset init bit */
#endif
    copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4);
    copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4);
    copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4);
    copy(info + I_MTIMOFF, hdr + F_MODDATE, 4);

    isfolder = f->compRMethod == startFolder && f->compDMethod == startFolder;
 
    /* list the file name if verbose or listonly mode, also if query mode */
    if (skip)   /* skip = 1 if skipping all in this folder */
      write_it = 0;
    else {
      write_it = 1;
      if (listonly || verbose || query) {
        for (i = 0; i < depth; i++)
          putchar(' ');

        if (isfolder) {
           printf("Folder: \"%s\"", uname);
        } else {
          if (listonly) {
            /* data fork, resource fork, name */
/*            
Original Packed Ratio Type  CRC  Original Packed Ratio Type  CRC  Name
-------- ------  ---  ----- ---- -------- ------  ---  ----- ---- ------------
*/
            printf("  %6d %6d %3d%%  -%3s- %04x ",
                   f->dataLength, f->compDLength,
                   (f->dataLength != 0) ? (100*f->compDLength)/f->dataLength : 0,
                   CompStr[ (f->compDMethod < 4) ? f->compDMethod : 4 ],
                   f->dataCRC);
            printf("  %6d %6d %3d%%  -%3s- %04x %s",
                   f->rsrcLength, f->compRLength,
                   (f->rsrcLength != 0) ? (100*f->compRLength)/f->rsrcLength : 0,
                   CompStr[ (f->compRMethod < 4) ? f->compRMethod : 4 ],
                   f->rsrcCRC, uname);
          } else {
              printf("name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld",
              uname, hdr + F_FTYPE, hdr + F_CREATOR, f->dataLength, f->rsrcLength);
          }
        }
        if (query) { /* if querying, check with the boss */
          printf(" ? ");
          fgets(temp, sizeof(temp) - 1, stdin);
          tp = temp;
          write_it = 0;
          while (*tp != '\0') {
            if (*tp == 'y' || *tp == 'Y') {
              write_it = 1;
              break;
            } else
              tp++;
          }
        } else  /* otherwise, terminate the line */
          putchar('\n');
      }
    }
    return(write_it ? H_WRITE : H_SKIP);
}

/* return 0 if OK to write on file fname, -1 otherwise */
check_access(char *fname) {
    char temp[10], *tp;

    if (access(fname, 0) == -1) {
      return(0);
    } else {
      printf("%s exists.  Overwrite? ", fname);
      fgets(temp, sizeof(temp) - 1, stdin);
      tp = temp;
      while (*tp != '\0') {
        if (*tp == 'y' || *tp == 'Y') {
          return(0);
        } else
          tp++;
      }
    }
    return(-1);
}

unsigned short
write_file(char *fname, long ibytes, long obytes, unsigned char type) {
    unsigned short crc;
    int i, n, ch, lastch;
    FILE *outf;
    char temp[256];
#ifdef AMIGA
    BPTR lock;
#endif

    crc = INIT_CRC;
    chkcrc = 1;   /* usually can check the CRC */

    if (check_access(fname) == -1) {
      fseek(infp, ibytes, 1);
      chkcrc = 0;  /* inhibit crc check if file not written */
      return(-1);
    }
 
    switch (type) {
      case noComp:  /* no compression */
        outf = fopen(fname, "w");
        if (outf == NULL) {
          perror(fname);
          exit(1);
        }
        while (ibytes > 0) {
          n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
          n = fread(iobuf, 1, n, infp);
          if (n == 0)
            break;
          crc = updcrc(crc, (unsigned char *)iobuf, n);
          outc(iobuf, n, outf);
          ibytes -= n;
        }
        fclose(outf);
        break;

      case rleComp:  /* run length encoding */
        outf = fopen(fname, "w");
        if (outf == NULL) {
          perror(fname);
          exit(1);
        }
        while (ibytes > 0) {
          ch = getc(infp) & 0xff;
          ibytes--;
          if (ch == 0x90) { /* see if its the repeat marker */
            n = getc(infp) & 0xff; /* get the repeat count */
            ibytes--;
            if (n == 0) { /* 0x90 was really an 0x90 */
              iobuf[0] = 0x90;
              crc = updcrc(crc, (unsigned char *)iobuf, 1);
              outc(iobuf, 1, outf);
            } else {
              n--;
              for (i = 0; i < n; i++)
                iobuf[i] = lastch;
              crc = updcrc(crc, (unsigned char *)iobuf, n);
              outc(iobuf, n, outf);
            }
          } else {
            iobuf[0] = ch;
            crc = updcrc(crc, (unsigned char *)iobuf, 1);
            lastch = ch;
            outc(iobuf, 1, outf);
          }
        }
        fclose(outf);
        break;

      case lzwComp:  /* LZW compression */

#ifdef AMIGA
       sprintf(temp, "%s%s%s%s", COMPRESS, " >\"", fname, "\" -d -c -n -b 14");
       /* sorry, we don't support txtmode yet */
#else
  	  sprintf(temp, "%s%s", COMPRESS, " -d -c -n -b 14 ");
	  if (txtmode) {
	      strcat(temp, "| tr \'\\015\' \'\\012\' ");
	      chkcrc = 0;		/* can't check CRC in this case */
	  }

	  strcat(temp, "> '");
	  strcat(temp, fname);
	  strcat(temp, "'");
#endif
	  outf = popen(temp, "w");
	  if (outf == NULL) {
	    perror(fname);
	    exit(1);
	  }
	  while (ibytes > 0) {
	    n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
	    n = fread(iobuf, 1, n, infp);
	    if (n == 0)
		break;
	    fwrite(iobuf, 1, n, outf);
	    ibytes -= n;
	  }
	  pclose(outf);
#ifdef AMIGA
       /* race condition...since "compress" is running asynchronously, it
        * might not be finished yet, by the time we get here...
        * ...I guess you can tell, there's a kludge coming up :-)
        */

       /* attempt lock (shared mode/reading)...proceed if ok,
        * wait & repeat, otherwise (because Lock() does not block).
        */
       while ((lock = Lock( fname, ACCESS_READ ))==NULL) {
         Delay(25); /* check every half second -- 50 ticks/sec */
       }
       UnLock(lock);
#endif
 	  if (chkcrc) {
	    outf = fopen(fname, "r"); /* read the file to get CRC value */
	    if (outf == NULL) {
		 perror(fname);
		 exit(1);
	    }
	    while (1) {
		 n = fread(iobuf, 1, IOBUFSIZ, outf);
		 if (n == 0)
		    break;
		 crc = updcrc(crc, (unsigned char *)iobuf, n);
	    }
	    fclose(outf);
	  }
	  break;

      case hufComp:  /* Huffman compression */
        outf = fopen(fname, "w");
        if (outf == NULL) {
          perror(fname);
          exit(1);
        }
        nodeptr = nodelist;
        bit = 0;  /* put us on a byte boundary */
        read_tree();
        while (obytes > 0) {
          n = (obytes > IOBUFSIZ) ? IOBUFSIZ : obytes;
          for (i = 0; i < n; i++)
            iobuf[i] = gethuffbyte(DECODE);
          crc = updcrc(crc, (unsigned char *)iobuf, n);
          outc(iobuf, n, outf);
          obytes -= n;
        }
        fclose(outf);
        break;

      default:
        fprintf(stderr, "Unknown/unsupported compression method: %d\n", type );
        chkcrc = 0;  /* inhibit crc check if file not written */
        return(-1);
    }

    return(crc & 0xffff);
}

void outc(char *p, int n, FILE *fp) {
    register char *p1;
    register int i;

    if (txtmode) {
      for (i = 0, p1 = p; i < n; i++, p1++)
        if ((*p1 & BYTEMASK) == '\r')
          *p1 = '\n';
    }
    fwrite(p, 1, n, fp);
}

long get4(char *bp) {
    register int i;
    long value = 0;

    for (i = 0; i < 4; i++) {
      value <<= 8;
      value |= (*bp & BYTEMASK);
      bp++;
    }
    return(value);
}

short get2(char *bp) {
    register int i;
    int value = 0;

    for (i = 0; i < 2; i++) {
      value <<= 8;
      value |= (*bp & BYTEMASK);
      bp++;
    }
    return(value);
}

void copy(char *p1, char *p2, int n) {
   while (n-- > 0)
     *p1++ = *p2++;
}

/* This routine recursively reads the Huffman encoding table and builds
   and decoding tree. */

struct node *read_tree()
{
   struct node *np;

   np = nodeptr++;
   if (getbit() == 1) {
      np->flag = 1;
      np->byte = gethuffbyte(NODECODE);
   } else {
      np->flag = 0;
      np->zero = read_tree();
      np->one  = read_tree();
   }
   return(np);
}


/* This routine returns the next bit in the input stream (MSB first) */
int getbit()
{
   static char b;

   if (bit == 0) {
      b = getc(infp) & 0xff;
      bit = 8;
   }
   bit--;
   return((b >> bit) & 1);
}

/* This routine returns the next 8 bits.  If decoding is on, it finds the
byte in the decoding tree based on the bits from the input stream.  If
decoding is not on, it either gets it directly from the input stream or
puts it together from 8 calls to getbit(), depending on whether or not we
are currently on a byte boundary
*/
int gethuffbyte(int decode) {
   register struct node *np;
   register int i, b;

   if (decode == DECODE) {
      np = nodelist;
      while (np->flag == 0)
         np = (getbit()) ? np->one : np->zero;
      b = np->byte;
   } else {
      if (bit == 0) /* on byte boundary? */
         b = getc(infp) & 0xff;
      else {  /* no, put a byte together */
         b = 0;
         for (i = 8; i > 0; i--) {
            b = (b << 1) + getbit();
         }
      }
   }
   return(b);
}
