home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Amiga Shareware Floppies / ma74.lha / MAShare74 / unsit1.5c.amiga / src / unsit.c < prev    next >
C/C++ Source or Header  |  1991-09-08  |  26KB  |  924 lines

  1. /*
  2.            unsit - Macintosh StuffIt file extractor
  3.  
  4.              Version 1.5c, for StuffIt 1.5
  5.  
  6.                 August 3, 1989
  7.  
  8. This program will unpack a Macintosh StuffIt file into separate files.
  9. The data fork of a StuffIt file contains both the data and resource
  10. forks of the packed files.  The program will unpack each Mac file into
  11. separate .data, .rsrc., and .info files that can be downloaded to a
  12. Mac using macput.  The program is much like the "unpit" program for
  13. breaking apart Packit archive files.
  14.  
  15.             ***** IMPORTANT *****
  16. To extract StuffIt files that have been compressed with the Lempel-Ziv
  17. compression method, unsit pipes the data through the "compress"
  18. program with the appropriate switches, rather than incorporate the
  19. uncompression routines within "unsit".  Therefore, it is necessary to
  20. have the "compress" program on the system and in the search path to
  21. make "unsit" work.  "Compress" is available from the comp.sources.unix
  22. archives.
  23.  
  24. The program syntax is much like unpit and macput/macget, with some added
  25. options:
  26.  
  27.     unsit [-rdulvqfm] stuffit-file.data
  28.  
  29. The -r and -d flags will cause only the resource and data forks to be
  30. written.  The -u flag will cause only the data fork to be written and
  31. to have carriage return characters changed to Unix newline characters.
  32. The -l flag will make the program only list the files in the StuffIt
  33. file.  The -v flag causes the program to list the names, sizes, type,
  34. and creators of the files it is writing.  The -q flag causes it to
  35. list the name, type and size of each file and wait for a 'y' or 'n'
  36. for either writing that file or skipping it, respectively.  The -m
  37. flag is used when the input file in in the MacBinary format instead of
  38. three separate .data, .info, and .rsrc files.  It causes the program
  39. to skip the 128 byte MacBinary header before looking for the StuffIt
  40. header.
  41.  
  42. Version 1.5 of the unsit supports extracting files and folders as
  43. implemented by StuffIt 1.5's "Hierarchy Maintained Folder" feature.
  44. Each folder is extracted as a subdirectory on the Unix system with the
  45. files in the folder placed in the corresponding subdirectory.  The -f
  46. option can be used to "flatten" out the hierarchy and unsit will store
  47. all the files in the current directory.  If the query option (-q) is
  48. used and a "n" response is given to a folder name, none of the files
  49. or folders in that folder will be extraced.
  50.  
  51. Some of the program is borrowed from the macput.c/macget.c programs.
  52. Many, many thanks to Raymond Lau, the author of StuffIt, for including
  53. information on the format of the StuffIt archives in the
  54. documentation.  Several changes and enhancements supplied by David
  55. Shanks (cde@atelabs.UUCP) have been incorporated into the program for
  56. doing things like supporting System V and recognizing MacBinary files.
  57. I'm always glad to receive advice, suggestions, or comments about the
  58. program so feel free to send whatever you think would be helpful
  59.  
  60.  
  61.     Author: Allan G. Weber
  62.         weber%brand.usc.edu@oberon.usc.edu
  63.         ...sdcrdcf!usc-oberon!brand!weber
  64.     Date:   April 3, 1989
  65.  
  66.                               - - - - - - - - - -
  67.  
  68.                         Amiga port (Saturday 10-Aug-91)
  69.                               mods by Anthon Pang
  70.  
  71.      1) '-u' option not supported (converting CR's to LF's in text files)
  72.      2) prints compression id when unknown (during extract)
  73.      3) '-l' option -- changed format of listings
  74.      4) ".sit" extension is optional for "filename.sit", unless file without
  75.         extension also exists (ie user should specify ".sit" explicitly)
  76.      5) included version 5.3 of "compress"...ported by Matt Dillon for uucp
  77.      6) changed extensions: ".data" -> ".d", ".rsrc" -> ".r", ".info" -> ".i"
  78.         (mainly because ".info" is already in use on the Amiga for icons)
  79.      7) some ANSI-cizing and reformatting
  80.  
  81.                      Addendum to Amiga Port (Sept 8, 1991)
  82.  
  83.      8) Corrected code for uncompressed lzw files, with spaces in names.
  84.      9) Using popen, pclose emulation package.
  85. */
  86.  
  87. #include <stdio.h>
  88.  
  89. #ifdef AMIGA
  90. #define SHORTNAMES
  91. #include <exec/types.h>
  92. #include <stat.h>
  93. #include <libraries/dosextens.h>
  94. #include "pio.h"
  95. #else
  96. #include <sys/types.h>
  97. #include <sys/stat.h>
  98. #endif
  99.  
  100. #include "unstuffit.h"
  101.  
  102. /*
  103.  * The following defines the name of the compress program that is used for the
  104.  * uncompression of Lempel-Ziv compressed files.  If the path is set up to
  105.  * include the right directory, this should work.
  106.  */
  107. #define COMPRESS  "compress"
  108.  
  109. #define IOBUFSIZ  4096
  110.  
  111. #define MACBINHDRSIZE  128L
  112.  
  113. #define INIT_CRC  0L
  114.  
  115. #define INFOBYTES  128
  116.  
  117. #ifndef AMIGA
  118. #define BYTEMASK 0xff
  119. #endif
  120.  
  121. #define S_SIGNATURE    0
  122. #define S_NUMFILES     4
  123. #define S_ARCLENGTH    6
  124. #define S_SIGNATURE2  10
  125. #define S_VERSION     14
  126. #define SITHDRSIZE    22
  127.  
  128. #define F_COMPRMETHOD    0
  129. #define F_COMPDMETHOD    1
  130. #define F_FNAME          2
  131. #define F_FTYPE         66
  132. #define F_CREATOR       70
  133. #define F_FNDRFLAGS     74
  134. #define F_CREATIONDATE  76
  135. #define F_MODDATE       80
  136. #define F_RSRCLENGTH    84
  137. #define F_DATALENGTH    88
  138. #define F_COMPRLENGTH   92
  139. #define F_COMPDLENGTH   96
  140. #define F_RSRCCRC      100
  141. #define F_DATACRC      102
  142. #define F_HDRCRC       110
  143. #define FILEHDRSIZE    112
  144.  
  145. #define F_NAMELEN  63
  146. #ifdef SHORTNAMES
  147. #ifdef AMIGA
  148. #define I_NAMELEN  31 /* 30 char file names + '\0' terminator */
  149. #else
  150. #define I_NAMELEN  15 /* 14 char file names + '\0' terminator */
  151. #endif
  152. #else
  153. #define I_NAMELEN  69 /* 63 + strlen (".info") + 1 */
  154. #endif
  155.  
  156. /* The following are copied out of macput.c/macget.c */
  157. #define I_NAMEOFF  1
  158. /* 65 <-> 80 is the FInfo structure */
  159. #define I_TYPEOFF  65
  160. #define I_AUTHOFF  69
  161. #define I_FLAGOFF  73
  162. #define I_LOCKOFF  81
  163. #define I_DLENOFF  83
  164. #define I_RLENOFF  87
  165. #define I_CTIMOFF  91
  166. #define I_MTIMOFF  95
  167.  
  168. #define INITED_BUG
  169. #define INITED_OFF  I_FLAGOFF /* offset to byte with Inited flag */
  170. #define INITED_MASK  (~1)     /* mask to '&' with byte to reset it */
  171.  
  172. #define TEXT 0
  173. #define DATA 1
  174. #define RSRC 2
  175. #define FULL 3
  176. #define DUMP 4
  177.  
  178. #define NODECODE 0
  179. #define DECODE   1
  180.  
  181. #define H_ERROR -1
  182. #define H_EOF    0
  183. #define H_WRITE  1
  184. #define H_SKIP   2
  185.  
  186. struct node {
  187.     int         flag, byte;
  188.     struct node *one, *zero;
  189.   } nodelist[512], *nodeptr, *read_tree(); /* 512 should be big enough */
  190.  
  191. struct sitHdr sithdr;
  192.  
  193. /* filenames */
  194. char f_info[I_NAMELEN];
  195. char f_data[I_NAMELEN];
  196. char f_rsrc[I_NAMELEN];
  197.  
  198. char info[INFOBYTES];
  199. char mname[F_NAMELEN+1];
  200. char uname[F_NAMELEN+1];
  201. char iobuf[IOBUFSIZ];
  202.  
  203. static char *CompStr[] = {
  204.      "NOT", "RLE", "LZW", "HUF", "???"
  205. };
  206.  
  207. int mode, txtmode, listonly, verbose, query, flatten;
  208. int bit, chkcrc, numfiles, depth;
  209. FILE *infp;
  210.  
  211.  
  212. /* function prototypes */
  213. extern unsigned short updcrc(unsigned short, unsigned char *, int);
  214. extern int getopt(int argc, char *argv[], char *optstring);
  215. void usage();
  216. int extract(char *parent, int skip);
  217. int extractfile(struct fileHdr *fh, int skip);
  218. int readsithdr(struct sitHdr *s);
  219. int readfilehdr(struct fileHdr *f, int skip);
  220. check_access(char *fname);
  221. unsigned short write_file(char *, long, long, unsigned char);
  222. void outc(char *p, int n, FILE *fp);
  223. long get4(char *bp);
  224. short get2(char *bp);
  225. void copy(char *p1, char *p2, int n);
  226. struct node *read_tree();
  227. int getbit();
  228. int gethuffbyte(int decode);
  229.  
  230.  
  231. void main(int argc, char *argv[]) {
  232.     extern int optind;
  233.     extern char *optarg;
  234.  
  235.     int status;
  236.     int c;
  237.     int errflg;
  238.     int macbin;
  239.     char temp[256];
  240.  
  241.     mode = FULL;
  242.     errflg = 0;
  243.     macbin = 0;
  244.     flatten = 0;
  245.     numfiles = 0;
  246.     depth = 0;
  247.  
  248.     while ((c = getopt(argc, argv, "dflmqruvx")) != EOF)
  249.       switch (c) {
  250.         case 'r':
  251.           mode = RSRC;
  252.           break;
  253.         case 'd':
  254.           mode = DATA;
  255.           break;
  256.         case 'u':
  257.           mode = TEXT;
  258.           break;
  259.         case 'l':
  260.           listonly++;
  261.           break;
  262.         case 'q':
  263.           query++;
  264.           break;
  265.         case 'v':
  266.           verbose++;
  267.           break;
  268.         case 'x':        /* undocumented */
  269.           mode = DUMP;
  270.           break;
  271.         case 'm':
  272.           macbin = 1;
  273.           break;
  274.         case 'f':
  275.           flatten = 1;
  276.           break;
  277.         case '?':
  278.           errflg++;
  279.           break;
  280.       }
  281.  
  282.     if (errflg) {
  283.       usage();
  284.       exit(1);
  285.     }
  286.  
  287.     if (optind == argc) {
  288.       usage();
  289.       exit(1);
  290.     } else {
  291.       if ((infp = fopen(argv[optind], "r")) == NULL) {
  292.         sprintf(temp, "%s.sit", argv[optind]);
  293.         if ((infp = fopen(temp, "r")) == NULL) {        
  294.            fprintf(stderr,"Can't open input file \"%s\"\n",argv[optind]);
  295.            exit(1);
  296.         }
  297.       }
  298.     }
  299.  
  300.     if (macbin) {
  301.       if (fseek(infp, MACBINHDRSIZE, 0) == -1) {
  302.         fprintf(stderr, "Can't skip over MacBinary header\n");
  303.         exit(1);
  304.       }
  305.     }
  306.  
  307.     if (readsithdr(&sithdr) == 0) {
  308.       fprintf(stderr, "Can't read file header\n");
  309.       exit(1);
  310.     }
  311.     if (listonly) {
  312.       printf("---------- Data Fork ----------- --------- Resource Fork -------- --------\n"
  313.              "Original Packed Ratio Type  CRC  Original Packed Ratio Type  CRC  Name\n"
  314.              "-------- ------  ---  ----- ---- -------- ------  ---  ----- ---- --------\n");
  315.  
  316.     }
  317.     /* 
  318.     printf("numfiles=%d, arclength=%ld\n", sithdr.numFiles, sithdr.arcLength);
  319.     */
  320.  
  321.     status = extract("", 0);
  322.     exit((status < 0) ? 1 : 0);
  323. }
  324.  
  325. void usage() {
  326.      fprintf(stderr, "Usage: unsit [-rdulvqmf] filename\n");
  327. }
  328.  
  329. /*
  330.   extract(parent, skip) - Extract all files from the current folder.
  331.   char *parent;           name of parent folder
  332.   int  skip;              1 to skip all files and folders in this one
  333.                           0 to extract them
  334.  
  335.   returns 1 if came an endFolder record
  336.           0 if EOF
  337.   -1 if error (bad fileHdr, bad file, etc.)
  338. */
  339.  
  340. int extract(char *parent, int skip) {
  341.     struct fileHdr filehdr;
  342.     struct stat sbuf;
  343.     int status, rstat, sstat, skipit;
  344.     char name[256];
  345.  
  346.     while (1) {
  347.       rstat = readfilehdr(&filehdr, skip);
  348.       if (rstat == H_ERROR || rstat == H_EOF) {
  349.         status = rstat;
  350.         break;
  351.       }
  352.  /*
  353.  printf("compr=%d, compd=%d, rsrclen=%ld, datalen=%ld, rsrccrc=%d, datacrc=%d\n",
  354.         filehdr.compRMethod, filehdr.compDMethod,
  355.         filehdr.compRLength, filehdr.compDLength,
  356.         filehdr.rsrcCRC, filehdr.dataCRC);
  357.  */
  358.  
  359.       skipit = (rstat == H_SKIP) ? 1 : 0;
  360.  
  361.       if (filehdr.compRMethod == endFolder && 
  362.         filehdr.compDMethod == endFolder) {
  363.         status = 1;  /* finished with this folder */
  364.         break;
  365.       } else if (filehdr.compRMethod == startFolder && 
  366.                  filehdr.compDMethod == startFolder) {
  367.         if (!listonly && rstat == H_WRITE && !flatten) {
  368.           sstat = stat(uname, &sbuf);
  369.           if (sstat == -1) { /* directory doesn't exist */
  370.             if (mkdir(uname, 0777) == -1) {
  371.               fprintf(stderr, "Can't create subdirectory %s\n", uname);
  372.               return(-1);
  373.             }
  374.           } else {  /* something exists with this name */
  375.             fprintf(stderr, "Directory name %s already in use\n", uname);
  376.             return(-1);
  377.           }
  378.           if (chdir(uname) == -1) {
  379.             fprintf(stderr, "Can't chdir to %s\n", uname);
  380.             return(-1);
  381.           }
  382.           sprintf(name,"%s:%s", parent, uname);
  383.         }
  384.         depth++;
  385.         status = extract(name, skipit);
  386.         depth--;
  387.         if (status != 1)
  388.           break;  /* problem with folder */
  389.         if (depth == 0) /* count how many top-level files done */
  390.           numfiles++;
  391.         if (!flatten)
  392. #ifdef AMIGA
  393.           chdir("/");
  394. #else
  395.           chdir("..");
  396. #endif
  397.       } else {
  398.         if ((status = extractfile(&filehdr, skipit)) != 1)
  399.           break;
  400.         if (depth == 0) /* count how many top-level files done */
  401.           numfiles++;
  402.       }
  403.       if (numfiles == sithdr.numFiles)
  404.         break;
  405.     }
  406.     return(status);
  407. }
  408.  
  409. int extractfile(struct fileHdr *fh, int skip) {
  410.     unsigned short crc;
  411.     FILE *fp;
  412.  
  413.     f_data[0] = f_rsrc[0] = f_info[0] = '\0'; /* assume no output files */
  414.     /* figure out what file names to use and what to do */
  415.     if (!listonly && !skip) {
  416.       switch (mode) {
  417.         case FULL:  /* do both rsrc and data forks */
  418.           sprintf(f_data, "%.*s.d", I_NAMELEN - 2, uname);
  419.           sprintf(f_rsrc, "%.*s.r", I_NAMELEN - 2, uname);
  420.           sprintf(f_info, "%.*s.i", I_NAMELEN - 2, uname);
  421.           break;
  422.         case RSRC:  /* rsrc fork only */
  423.           sprintf(f_rsrc, "%.*s.r", I_NAMELEN - 2, uname);
  424.           break;
  425.         case DATA:  /* data fork only */
  426.         case TEXT:
  427.           sprintf(f_data, "%.*s", I_NAMELEN - 1, uname);
  428.           break;
  429.         case DUMP:  /* for debugging, dump data as is */
  430.           sprintf(f_data, "%.*s.ddump", I_NAMELEN - 7, uname);
  431.           sprintf(f_rsrc, "%.*s.rdump", I_NAMELEN - 7, uname);
  432.           fh->compRMethod = fh->compDMethod = noComp;
  433.           break;
  434.       }
  435.     }
  436.  
  437.     if (f_info[0] != '\0' && check_access(f_info) != -1) {
  438.       fp = fopen(f_info, "w");
  439.       if (fp == NULL) {
  440.         perror(f_info);
  441.         exit(1);
  442.       }
  443.       fwrite(info, 1, INFOBYTES, fp);
  444.       fclose(fp);
  445.     }
  446.  
  447.     if (f_rsrc[0] != '\0') {
  448.       txtmode = 0;
  449.       crc = write_file(f_rsrc, fh->compRLength, fh->rsrcLength, fh->compRMethod);
  450.       if (chkcrc && fh->rsrcCRC != crc) {
  451.         fprintf(stderr, "CRC error on resource fork: need 0x%04x, got 0x%04x\n", fh->rsrcCRC, crc);
  452.         return(-1);
  453.       }
  454.     } else {
  455.       fseek(infp, (long) fh->compRLength, 1);
  456.     }
  457.  
  458.     if (f_data[0] != '\0') {
  459.       txtmode = (mode == TEXT);
  460.       crc = write_file(f_data, fh->compDLength, fh->dataLength, fh->compDMethod);
  461.       if (chkcrc && fh->dataCRC != crc) {
  462.         fprintf(stderr, "CRC error on data fork: need 0x%04x, got 0x%04x\n", fh->dataCRC, crc);
  463.         return(-1);
  464.       }
  465.     } else {
  466.       fseek(infp, (long) fh->compDLength, 1);
  467.     }
  468.     return(1);
  469. }
  470.  
  471. int readsithdr(struct sitHdr *s) {
  472.     char temp[FILEHDRSIZE];
  473.     int count = 0;
  474.  
  475.     for (;;) {
  476.       if (fread(temp, 1, SITHDRSIZE, infp) != SITHDRSIZE) {
  477.         fprintf(stderr, "Can't read file header\n");
  478.         return(0);
  479.       }
  480.      
  481.       if (strncmp(temp + S_SIGNATURE,  "SIT!", 4) == 0 &&
  482.                strncmp(temp + S_SIGNATURE2, "rLau", 4) == 0) {
  483.         s->numFiles = get2(temp + S_NUMFILES);
  484.         s->arcLength = get4(temp + S_ARCLENGTH);
  485.         return(1);
  486.       }
  487.     
  488.       if (++count == 2) {
  489.         fprintf(stderr, "Not a StuffIt file\n");
  490.         return(0);
  491.       }
  492.  
  493.       if (fread(&temp[SITHDRSIZE], 1, FILEHDRSIZE - SITHDRSIZE, infp) !=
  494.                                                 FILEHDRSIZE - SITHDRSIZE) {
  495.         fprintf(stderr, "Can't read file header\n");
  496.         return(0);
  497.       }
  498.     
  499.       if (strncmp(temp + I_TYPEOFF, "SIT!", 4) == 0 &&
  500.           strncmp(temp + I_AUTHOFF, "SIT!", 4) == 0) { /* MacBinary format */
  501.         fseek(infp, (long)(INFOBYTES-FILEHDRSIZE), 1); /* Skip over header */
  502.       }
  503.    }
  504. }
  505.  
  506. /*
  507.   readfilehdr - reads the file header for each file and the folder start
  508.   and end records.
  509.  
  510.   returns: H_ERROR = error
  511.     H_EOF   = EOF
  512.     H_WRITE = write file/folder
  513.     H_SKIP  = skip file/folder
  514. */
  515.  
  516. int readfilehdr(struct fileHdr *f, int skip) {
  517.     unsigned short crc;
  518.     int i, n, write_it, isfolder;
  519.     char hdr[FILEHDRSIZE];
  520.     char ch, *mp, *up;
  521.     char *tp, temp[10];
  522.  
  523.     for (i = 0; i < INFOBYTES; i++)
  524.       info[i] = '\0';
  525.  
  526.     /* read in the next file header, which could be folder start/end record */
  527.     n = fread(hdr, 1, FILEHDRSIZE, infp);
  528.     if (n == 0)   /* return 0 on EOF */
  529.       return(H_EOF);
  530.     else if (n != FILEHDRSIZE) {
  531.       fprintf(stderr, "Can't read file header\n");
  532.       return(H_ERROR);
  533.     }
  534.  
  535.     /* check the CRC for the file header */
  536.     crc = INIT_CRC;
  537.     crc = updcrc(crc, (unsigned char *)hdr, FILEHDRSIZE - 2L);
  538.     f->hdrCRC = get2(hdr + F_HDRCRC);
  539.     if (f->hdrCRC != crc) {
  540.       fprintf(stderr, "Header CRC mismatch: got 0x%04x, need 0x%04x\n", f->hdrCRC, crc);
  541.       return(H_ERROR);
  542.     }
  543.  
  544.     /* grab the name of the file or folder */
  545.     n = hdr[F_FNAME] & BYTEMASK;
  546.     if (n > F_NAMELEN)
  547.       n = F_NAMELEN;
  548.     info[I_NAMEOFF] = n;
  549.     copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n);
  550.     strncpy(mname, hdr + F_FNAME + 1, n);
  551.     mname[n] = '\0';
  552.     /* copy to a string with no illegal Unix characters in the file name */
  553.     mp = mname;
  554.     up = uname;
  555.     while ((ch = *mp++) != '\0') {
  556.       if (ch <= ' ' || ch > '~' || index("/!()[]*<>?\\\"$\';&`", ch) != NULL)
  557.         ch = '_';
  558.       *up++ = ch;
  559.     }
  560.     *up = '\0';
  561.  
  562.     /* get lots of other stuff from the header */
  563.     f->compRMethod = hdr[F_COMPRMETHOD];
  564.     f->compDMethod = hdr[F_COMPDMETHOD];
  565.     f->rsrcLength = get4(hdr + F_RSRCLENGTH);
  566.     f->dataLength = get4(hdr + F_DATALENGTH);
  567.     f->compRLength = get4(hdr + F_COMPRLENGTH);
  568.     f->compDLength = get4(hdr + F_COMPDLENGTH);
  569.     f->rsrcCRC = get2(hdr + F_RSRCCRC);
  570.     f->dataCRC = get2(hdr + F_DATACRC);
  571.  
  572.     /* if it's an end folder record, don't need to do any more */
  573.     if (f->compRMethod == endFolder && f->compDMethod == endFolder)
  574.       return(H_WRITE);
  575.  
  576.     /* prepare an info file in case its needed */
  577.  
  578.     copy(info + I_TYPEOFF, hdr + F_FTYPE, 4);
  579.     copy(info + I_AUTHOFF, hdr + F_CREATOR, 4);
  580.     copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2);
  581. #ifdef INITED_BUG
  582.     info[INITED_OFF] &= INITED_MASK; /* reset init bit */
  583. #endif
  584.     copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4);
  585.     copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4);
  586.     copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4);
  587.     copy(info + I_MTIMOFF, hdr + F_MODDATE, 4);
  588.  
  589.     isfolder = f->compRMethod == startFolder && f->compDMethod == startFolder;
  590.  
  591.     /* list the file name if verbose or listonly mode, also if query mode */
  592.     if (skip)   /* skip = 1 if skipping all in this folder */
  593.       write_it = 0;
  594.     else {
  595.       write_it = 1;
  596.       if (listonly || verbose || query) {
  597.         for (i = 0; i < depth; i++)
  598.           putchar(' ');
  599.  
  600.         if (isfolder) {
  601.            printf("Folder: \"%s\"", uname);
  602.         } else {
  603.           if (listonly) {
  604.             /* data fork, resource fork, name */
  605. /*            
  606. Original Packed Ratio Type  CRC  Original Packed Ratio Type  CRC  Name
  607. -------- ------  ---  ----- ---- -------- ------  ---  ----- ---- ------------
  608. */
  609.             printf("  %6d %6d %3d%%  -%3s- %04x ",
  610.                    f->dataLength, f->compDLength,
  611.                    (f->dataLength != 0) ? (100*f->compDLength)/f->dataLength : 0,
  612.                    CompStr[ (f->compDMethod < 4) ? f->compDMethod : 4 ],
  613.                    f->dataCRC);
  614.             printf("  %6d %6d %3d%%  -%3s- %04x %s",
  615.                    f->rsrcLength, f->compRLength,
  616.                    (f->rsrcLength != 0) ? (100*f->compRLength)/f->rsrcLength : 0,
  617.                    CompStr[ (f->compRMethod < 4) ? f->compRMethod : 4 ],
  618.                    f->rsrcCRC, uname);
  619.           } else {
  620.               printf("name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld",
  621.               uname, hdr + F_FTYPE, hdr + F_CREATOR, f->dataLength, f->rsrcLength);
  622.           }
  623.         }
  624.         if (query) { /* if querying, check with the boss */
  625.           printf(" ? ");
  626.           fgets(temp, sizeof(temp) - 1, stdin);
  627.           tp = temp;
  628.           write_it = 0;
  629.           while (*tp != '\0') {
  630.             if (*tp == 'y' || *tp == 'Y') {
  631.               write_it = 1;
  632.               break;
  633.             } else
  634.               tp++;
  635.           }
  636.         } else  /* otherwise, terminate the line */
  637.           putchar('\n');
  638.       }
  639.     }
  640.     return(write_it ? H_WRITE : H_SKIP);
  641. }
  642.  
  643. /* return 0 if OK to write on file fname, -1 otherwise */
  644. check_access(char *fname) {
  645.     char temp[10], *tp;
  646.  
  647.     if (access(fname, 0) == -1) {
  648.       return(0);
  649.     } else {
  650.       printf("%s exists.  Overwrite? ", fname);
  651.       fgets(temp, sizeof(temp) - 1, stdin);
  652.       tp = temp;
  653.       while (*tp != '\0') {
  654.         if (*tp == 'y' || *tp == 'Y') {
  655.           return(0);
  656.         } else
  657.           tp++;
  658.       }
  659.     }
  660.     return(-1);
  661. }
  662.  
  663. unsigned short
  664. write_file(char *fname, long ibytes, long obytes, unsigned char type) {
  665.     unsigned short crc;
  666.     int i, n, ch, lastch;
  667.     FILE *outf;
  668.     char temp[256];
  669. #ifdef AMIGA
  670.     BPTR lock;
  671. #endif
  672.  
  673.     crc = INIT_CRC;
  674.     chkcrc = 1;   /* usually can check the CRC */
  675.  
  676.     if (check_access(fname) == -1) {
  677.       fseek(infp, ibytes, 1);
  678.       chkcrc = 0;  /* inhibit crc check if file not written */
  679.       return(-1);
  680.     }
  681.  
  682.     switch (type) {
  683.       case noComp:  /* no compression */
  684.         outf = fopen(fname, "w");
  685.         if (outf == NULL) {
  686.           perror(fname);
  687.           exit(1);
  688.         }
  689.         while (ibytes > 0) {
  690.           n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
  691.           n = fread(iobuf, 1, n, infp);
  692.           if (n == 0)
  693.             break;
  694.           crc = updcrc(crc, (unsigned char *)iobuf, n);
  695.           outc(iobuf, n, outf);
  696.           ibytes -= n;
  697.         }
  698.         fclose(outf);
  699.         break;
  700.  
  701.       case rleComp:  /* run length encoding */
  702.         outf = fopen(fname, "w");
  703.         if (outf == NULL) {
  704.           perror(fname);
  705.           exit(1);
  706.         }
  707.         while (ibytes > 0) {
  708.           ch = getc(infp) & 0xff;
  709.           ibytes--;
  710.           if (ch == 0x90) { /* see if its the repeat marker */
  711.             n = getc(infp) & 0xff; /* get the repeat count */
  712.             ibytes--;
  713.             if (n == 0) { /* 0x90 was really an 0x90 */
  714.               iobuf[0] = 0x90;
  715.               crc = updcrc(crc, (unsigned char *)iobuf, 1);
  716.               outc(iobuf, 1, outf);
  717.             } else {
  718.               n--;
  719.               for (i = 0; i < n; i++)
  720.                 iobuf[i] = lastch;
  721.               crc = updcrc(crc, (unsigned char *)iobuf, n);
  722.               outc(iobuf, n, outf);
  723.             }
  724.           } else {
  725.             iobuf[0] = ch;
  726.             crc = updcrc(crc, (unsigned char *)iobuf, 1);
  727.             lastch = ch;
  728.             outc(iobuf, 1, outf);
  729.           }
  730.         }
  731.         fclose(outf);
  732.         break;
  733.  
  734.       case lzwComp:  /* LZW compression */
  735.  
  736. #ifdef AMIGA
  737.        sprintf(temp, "%s%s%s%s", COMPRESS, " >\"", fname, "\" -d -c -n -b 14");
  738.        /* sorry, we don't support txtmode yet */
  739. #else
  740.         sprintf(temp, "%s%s", COMPRESS, " -d -c -n -b 14 ");
  741.       if (txtmode) {
  742.           strcat(temp, "| tr \'\\015\' \'\\012\' ");
  743.           chkcrc = 0;        /* can't check CRC in this case */
  744.       }
  745.  
  746.       strcat(temp, "> '");
  747.       strcat(temp, fname);
  748.       strcat(temp, "'");
  749. #endif
  750.       outf = popen(temp, "w");
  751.       if (outf == NULL) {
  752.         perror(fname);
  753.         exit(1);
  754.       }
  755.       while (ibytes > 0) {
  756.         n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
  757.         n = fread(iobuf, 1, n, infp);
  758.         if (n == 0)
  759.         break;
  760.         fwrite(iobuf, 1, n, outf);
  761.         ibytes -= n;
  762.       }
  763.       pclose(outf);
  764. #ifdef AMIGA
  765.        /* race condition...since "compress" is running asynchronously, it
  766.         * might not be finished yet, by the time we get here...
  767.         * ...I guess you can tell, there's a kludge coming up :-)
  768.         */
  769.  
  770.        /* attempt lock (shared mode/reading)...proceed if ok,
  771.         * wait & repeat, otherwise (because Lock() does not block).
  772.         */
  773.        while ((lock = Lock( fname, ACCESS_READ ))==NULL) {
  774.          Delay(25); /* check every half second -- 50 ticks/sec */
  775.        }
  776.        UnLock(lock);
  777. #endif
  778.        if (chkcrc) {
  779.         outf = fopen(fname, "r"); /* read the file to get CRC value */
  780.         if (outf == NULL) {
  781.          perror(fname);
  782.          exit(1);
  783.         }
  784.         while (1) {
  785.          n = fread(iobuf, 1, IOBUFSIZ, outf);
  786.          if (n == 0)
  787.             break;
  788.          crc = updcrc(crc, (unsigned char *)iobuf, n);
  789.         }
  790.         fclose(outf);
  791.       }
  792.       break;
  793.  
  794.       case hufComp:  /* Huffman compression */
  795.         outf = fopen(fname, "w");
  796.         if (outf == NULL) {
  797.           perror(fname);
  798.           exit(1);
  799.         }
  800.         nodeptr = nodelist;
  801.         bit = 0;  /* put us on a byte boundary */
  802.         read_tree();
  803.         while (obytes > 0) {
  804.           n = (obytes > IOBUFSIZ) ? IOBUFSIZ : obytes;
  805.           for (i = 0; i < n; i++)
  806.             iobuf[i] = gethuffbyte(DECODE);
  807.           crc = updcrc(crc, (unsigned char *)iobuf, n);
  808.           outc(iobuf, n, outf);
  809.           obytes -= n;
  810.         }
  811.         fclose(outf);
  812.         break;
  813.  
  814.       default:
  815.         fprintf(stderr, "Unknown/unsupported compression method: %d\n", type );
  816.         chkcrc = 0;  /* inhibit crc check if file not written */
  817.         return(-1);
  818.     }
  819.  
  820.     return(crc & 0xffff);
  821. }
  822.  
  823. void outc(char *p, int n, FILE *fp) {
  824.     register char *p1;
  825.     register int i;
  826.  
  827.     if (txtmode) {
  828.       for (i = 0, p1 = p; i < n; i++, p1++)
  829.         if ((*p1 & BYTEMASK) == '\r')
  830.           *p1 = '\n';
  831.     }
  832.     fwrite(p, 1, n, fp);
  833. }
  834.  
  835. long get4(char *bp) {
  836.     register int i;
  837.     long value = 0;
  838.  
  839.     for (i = 0; i < 4; i++) {
  840.       value <<= 8;
  841.       value |= (*bp & BYTEMASK);
  842.       bp++;
  843.     }
  844.     return(value);
  845. }
  846.  
  847. short get2(char *bp) {
  848.     register int i;
  849.     int value = 0;
  850.  
  851.     for (i = 0; i < 2; i++) {
  852.       value <<= 8;
  853.       value |= (*bp & BYTEMASK);
  854.       bp++;
  855.     }
  856.     return(value);
  857. }
  858.  
  859. void copy(char *p1, char *p2, int n) {
  860.    while (n-- > 0)
  861.      *p1++ = *p2++;
  862. }
  863.  
  864. /* This routine recursively reads the Huffman encoding table and builds
  865.    and decoding tree. */
  866.  
  867. struct node *read_tree()
  868. {
  869.    struct node *np;
  870.  
  871.    np = nodeptr++;
  872.    if (getbit() == 1) {
  873.       np->flag = 1;
  874.       np->byte = gethuffbyte(NODECODE);
  875.    } else {
  876.       np->flag = 0;
  877.       np->zero = read_tree();
  878.       np->one  = read_tree();
  879.    }
  880.    return(np);
  881. }
  882.  
  883.  
  884. /* This routine returns the next bit in the input stream (MSB first) */
  885. int getbit()
  886. {
  887.    static char b;
  888.  
  889.    if (bit == 0) {
  890.       b = getc(infp) & 0xff;
  891.       bit = 8;
  892.    }
  893.    bit--;
  894.    return((b >> bit) & 1);
  895. }
  896.  
  897. /* This routine returns the next 8 bits.  If decoding is on, it finds the
  898. byte in the decoding tree based on the bits from the input stream.  If
  899. decoding is not on, it either gets it directly from the input stream or
  900. puts it together from 8 calls to getbit(), depending on whether or not we
  901. are currently on a byte boundary
  902. */
  903. int gethuffbyte(int decode) {
  904.    register struct node *np;
  905.    register int i, b;
  906.  
  907.    if (decode == DECODE) {
  908.       np = nodelist;
  909.       while (np->flag == 0)
  910.          np = (getbit()) ? np->one : np->zero;
  911.       b = np->byte;
  912.    } else {
  913.       if (bit == 0) /* on byte boundary? */
  914.          b = getc(infp) & 0xff;
  915.       else {  /* no, put a byte together */
  916.          b = 0;
  917.          for (i = 8; i > 0; i--) {
  918.             b = (b << 1) + getbit();
  919.          }
  920.       }
  921.    }
  922.    return(b);
  923. }
  924.