home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / source / unsit_15.shr / unsit.c < prev    next >
C/C++ Source or Header  |  1991-05-20  |  21KB  |  867 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. #include <stdio.h>
  69. #include <sys/types.h>
  70. #include <sys/stat.h>
  71.  
  72. typedef long OSType;
  73.  
  74. #include "stuffit.h"
  75.  
  76. /*
  77.  * Define the following if your Unix can only handle 14 character file names
  78.  * (e.g. Version 7 and System V).
  79.  */
  80. /* #define SHORTNAMES */
  81.  
  82. /*
  83.  * The following defines the name of the compress program that is used for the
  84.  * uncompression of Lempel-Ziv compressed files.  If the path is set up to
  85.  * include the right directory, this should work.
  86.  */
  87. #define COMPRESS   "compress"
  88.  
  89. #define IOBUFSIZ   4096
  90.  
  91. #define MACBINHDRSIZE  128L
  92.  
  93. #define INIT_CRC 0L
  94. extern unsigned short updcrc();
  95.  
  96. #define INFOBYTES 128
  97.  
  98. #define BYTEMASK 0xff
  99.  
  100. #define S_SIGNATURE    0
  101. #define S_NUMFILES     4
  102. #define S_ARCLENGTH    6
  103. #define S_SIGNATURE2  10
  104. #define    S_VERSION     14
  105. #define SITHDRSIZE    22
  106.  
  107. #define F_COMPRMETHOD    0
  108. #define F_COMPDMETHOD    1
  109. #define F_FNAME          2
  110. #define F_FTYPE         66
  111. #define F_CREATOR       70
  112. #define F_FNDRFLAGS     74
  113. #define F_CREATIONDATE  76
  114. #define F_MODDATE       80
  115. #define F_RSRCLENGTH    84
  116. #define F_DATALENGTH    88
  117. #define F_COMPRLENGTH   92
  118. #define F_COMPDLENGTH   96
  119. #define F_RSRCCRC      100
  120. #define F_DATACRC      102
  121. #define F_HDRCRC       110
  122. #define FILEHDRSIZE    112
  123.  
  124. #define F_NAMELEN 63
  125. #ifdef SHORTNAMES        /* short file names */
  126. # define I_NAMELEN 15        /* 14 char file names + '\0' terminator */
  127. #else
  128. # define I_NAMELEN 69        /* 63 + strlen(".info") + 1 */
  129. #endif
  130.  
  131. /* The following are copied out of macput.c/macget.c */
  132. #define I_NAMEOFF 1
  133. /* 65 <-> 80 is the FInfo structure */
  134. #define I_TYPEOFF 65
  135. #define I_AUTHOFF 69
  136. #define I_FLAGOFF 73
  137. #define I_LOCKOFF 81
  138. #define I_DLENOFF 83
  139. #define I_RLENOFF 87
  140. #define I_CTIMOFF 91
  141. #define I_MTIMOFF 95
  142.  
  143. #define INITED_BUG
  144. #define INITED_OFF    I_FLAGOFF    /* offset to byte with Inited flag */
  145. #define INITED_MASK    (~1)        /* mask to '&' with byte to reset it */
  146.  
  147. #define TEXT 0
  148. #define DATA 1
  149. #define RSRC 2
  150. #define FULL 3
  151. #define DUMP 4
  152.  
  153. #define NODECODE 0
  154. #define DECODE   1
  155.  
  156. #define H_ERROR -1
  157. #define H_EOF    0
  158. #define H_WRITE  1
  159. #define H_SKIP   2
  160.  
  161. struct node {
  162.     int flag, byte;
  163.     struct node *one, *zero;
  164. } nodelist[512], *nodeptr, *read_tree();    /* 512 should be big enough */
  165.  
  166. struct sitHdr sithdr;
  167.  
  168. char f_info[I_NAMELEN];
  169. char f_data[I_NAMELEN];
  170. char f_rsrc[I_NAMELEN];
  171.  
  172. char info[INFOBYTES];
  173. char mname[F_NAMELEN+1];
  174. char uname[F_NAMELEN+1];
  175. char iobuf[IOBUFSIZ];
  176.  
  177. int mode, txtmode, listonly, verbose, query, flatten;
  178. int bit, chkcrc, numfiles, depth;
  179. FILE *infp;
  180.  
  181. long get4();
  182. short get2();
  183. unsigned short write_file();
  184.  
  185. main(argc, argv)
  186. int argc;
  187. char *argv[];
  188. {
  189.     int status;
  190.     int c;
  191.     extern int optind;
  192.     extern char *optarg;
  193.     int errflg;
  194.     int macbin;
  195.  
  196.     mode = FULL;
  197.     errflg = 0;
  198.     macbin = 0;
  199.     flatten = 0;
  200.     numfiles = 0;
  201.     depth = 0;
  202.  
  203.     while ((c = getopt(argc, argv, "dflmqruvx")) != EOF)
  204.     switch (c) {
  205.       case 'r':
  206.         mode = RSRC;
  207.         break;
  208.       case 'd':
  209.         mode = DATA;
  210.         break;
  211.       case 'u':
  212.         mode = TEXT;
  213.         break;
  214.       case 'l':
  215.         listonly++;
  216.         break;
  217.       case 'q':
  218.         query++;
  219.         break;
  220.       case 'v':
  221.         verbose++;
  222.         break;
  223.       case 'x':
  224.         mode = DUMP;
  225.         break;
  226.       case 'm':
  227.         macbin = 1;
  228.         break;
  229.       case 'f':
  230.         flatten = 1;
  231.         break;
  232.       case '?':
  233.         errflg++;
  234.         break;
  235.     }
  236.     if (errflg) {
  237.     usage();
  238.     exit(1);
  239.     }
  240.  
  241.     if (optind == argc) {
  242.     usage();
  243.     exit(1);
  244.     }
  245.     else {
  246.     if ((infp = fopen(argv[optind], "r")) == NULL) {
  247.         fprintf(stderr,"Can't open input file \"%s\"\n",argv[optind]);
  248.         exit(1);
  249.     }
  250.     }
  251.  
  252.     if (macbin) {
  253.     if (fseek(infp, MACBINHDRSIZE, 0) == -1) {
  254.         fprintf(stderr, "Can't skip over MacBinary header\n");
  255.         exit(1);
  256.     }
  257.     }
  258.  
  259.     if (readsithdr(&sithdr) == 0) {
  260.     fprintf(stderr, "Can't read file header\n");
  261.     exit(1);
  262.     }
  263.     /* 
  264.     printf("numfiles=%d, arclength=%ld\n", sithdr.numFiles, sithdr.arcLength);
  265.     */
  266.     
  267.     status = extract("", 0);
  268.     exit((status < 0) ? 1 : 0);
  269. }
  270.  
  271. usage()
  272. {
  273.     fprintf(stderr, "Usage: unsit [-rdulvqmf] filename\n");
  274. }
  275.  
  276. /*
  277.   extract(parent, skip) - Extract all files from the current folder.
  278.   char *parent;           name of parent folder
  279.   int  skip;              1 to skip all files and folders in this one
  280.                           0 to extract them
  281.  
  282.   returns 1 if came an endFolder record
  283.           0 if EOF
  284.      -1 if error (bad fileHdr, bad file, etc.)
  285. */
  286.  
  287. extract(parent, skip)
  288. char *parent;
  289. int skip;
  290. {
  291.     struct fileHdr filehdr;
  292.     struct stat sbuf;
  293.     int status, rstat, sstat, skipit;
  294.     char name[256];
  295.  
  296.     while (1) {
  297.     rstat = readfilehdr(&filehdr, skip);
  298.     if (rstat == H_ERROR || rstat == H_EOF) {
  299.         status = rstat;
  300.         break;
  301.     }
  302.     /*
  303.     printf("compr=%d, compd=%d, rsrclen=%ld, datalen=%ld, rsrccrc=%d, datacrc=%d\n",
  304.            filehdr.compRMethod, filehdr.compDMethod,
  305.            filehdr.compRLength, filehdr.compDLength,
  306.            filehdr.rsrcCRC, filehdr.dataCRC);
  307.     */
  308.  
  309.     skipit = (rstat == H_SKIP) ? 1 : 0;
  310.  
  311.     if (filehdr.compRMethod == endFolder && 
  312.         filehdr.compDMethod == endFolder) {
  313.         status = 1;        /* finished with this folder */
  314.         break;
  315.     }
  316.     else if (filehdr.compRMethod == startFolder && 
  317.          filehdr.compDMethod == startFolder) {
  318.         if (!listonly && rstat == H_WRITE && !flatten) {
  319.         sstat = stat(uname, &sbuf);
  320.         if (sstat == -1) {    /* directory doesn't exist */
  321.             if (mkdir(uname, 0777) == -1) {
  322.             fprintf(stderr,
  323.                 "Can't create subdirectory %s\n", uname);
  324.             return(-1);
  325.             }
  326.         }
  327.         else {        /* something exists with this name */
  328.             if ((sbuf.st_mode & S_IFMT) != S_IFDIR) {
  329.             fprintf(stderr, "Directory name %s already in use\n",
  330.                 uname);
  331.             return(-1);
  332.             }
  333.         }
  334.         if (chdir(uname) == -1) {
  335.             fprintf(stderr, "Can't chdir to %s\n", uname);
  336.             return(-1);
  337.         }
  338.         sprintf(name,"%s:%s", parent, uname);
  339.         }
  340.         depth++;
  341.         status = extract(name, skipit);
  342.         depth--;
  343.         if (status != 1)
  344.         break;        /* problem with folder */
  345.         if (depth == 0)    /* count how many top-level files done */
  346.         numfiles++;
  347.         if (!flatten)
  348.         chdir("..");
  349.     }
  350.     else {
  351.         if ((status = extractfile(&filehdr, skipit)) != 1)
  352.         break;
  353.         if (depth == 0)    /* count how many top-level files done */
  354.         numfiles++;
  355.     }
  356.     if (numfiles == sithdr.numFiles)
  357.         break;
  358.     }
  359.     return(status);
  360. }
  361.  
  362. extractfile(fh, skip)
  363. struct fileHdr *fh;
  364. int skip;
  365. {
  366.     unsigned short crc;
  367.     FILE *fp;
  368.  
  369.     f_data[0] = f_rsrc[0] = f_info[0] = '\0'; /* assume no output files */
  370.     /* figure out what file names to use and what to do */
  371.     if (!listonly && !skip) {
  372.     switch (mode) {
  373.       case FULL:        /* do both rsrc and data forks */
  374.         sprintf(f_data, "%.*s.data", I_NAMELEN - 6, uname);
  375.         sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname);
  376.         sprintf(f_info, "%.*s.info", I_NAMELEN - 6, uname);
  377.         break;
  378.       case RSRC:        /* rsrc fork only */
  379.         sprintf(f_rsrc, "%.*s.rsrc", I_NAMELEN - 6, uname);
  380.         break;
  381.       case DATA:        /* data fork only */
  382.       case TEXT:
  383.         sprintf(f_data, "%.*s", I_NAMELEN - 1, uname);
  384.         break;
  385.       case DUMP:        /* for debugging, dump data as is */
  386.         sprintf(f_data, "%.*s.ddump", I_NAMELEN - 7, uname);
  387.         sprintf(f_rsrc, "%.*s.rdump", I_NAMELEN - 7, uname);
  388.         fh->compRMethod = fh->compDMethod = noComp;
  389.         break;
  390.     }
  391.     }
  392.  
  393.     if (f_info[0] != '\0' && check_access(f_info) != -1) {
  394.     fp = fopen(f_info, "w");
  395.     if (fp == NULL) {
  396.         perror(f_info);
  397.         exit(1);
  398.     }
  399.     fwrite(info, 1, INFOBYTES, fp);
  400.     fclose(fp);
  401.     }
  402.  
  403.     if (f_rsrc[0] != '\0') {
  404.     txtmode = 0;
  405.     crc = write_file(f_rsrc, fh->compRLength,
  406.              fh->rsrcLength, fh->compRMethod);
  407.     if (chkcrc && fh->rsrcCRC != crc) {
  408.         fprintf(stderr,
  409.             "CRC error on resource fork: need 0x%04x, got 0x%04x\n",
  410.             fh->rsrcCRC, crc);
  411.         return(-1);
  412.     }
  413.     }
  414.     else {
  415.     fseek(infp, (long) fh->compRLength, 1);
  416.     }
  417.     if (f_data[0] != '\0') {
  418.     txtmode = (mode == TEXT);
  419.     crc = write_file(f_data, fh->compDLength,
  420.              fh->dataLength, fh->compDMethod);
  421.     if (chkcrc && fh->dataCRC != crc) {
  422.         fprintf(stderr,
  423.             "CRC error on data fork: need 0x%04x, got 0x%04x\n",
  424.             fh->dataCRC, crc);
  425.         return(-1);
  426.     }
  427.     }
  428.     else {
  429.     fseek(infp, (long) fh->compDLength, 1);
  430.     }
  431.     return(1);
  432. }
  433.  
  434. readsithdr(s)
  435. struct sitHdr *s;
  436. {
  437.     char temp[FILEHDRSIZE];
  438.     int count = 0;
  439.  
  440.     for (;;) {
  441.     if (fread(temp, 1, SITHDRSIZE, infp) != SITHDRSIZE) {
  442.         fprintf(stderr, "Can't read file header\n");
  443.         return(0);
  444.     }
  445.         
  446.     if (strncmp(temp + S_SIGNATURE,  "SIT!", 4) == 0 &&
  447.         strncmp(temp + S_SIGNATURE2, "rLau", 4) == 0) {
  448.         s->numFiles = get2(temp + S_NUMFILES);
  449.         s->arcLength = get4(temp + S_ARCLENGTH);
  450.         return(1);
  451.     }
  452.     
  453.     if (++count == 2) {
  454.         fprintf(stderr, "Not a StuffIt file\n");
  455.         return(0);
  456.     }
  457.     
  458.     if (fread(&temp[SITHDRSIZE], 1, FILEHDRSIZE - SITHDRSIZE, infp) !=
  459.         FILEHDRSIZE - SITHDRSIZE) {
  460.         fprintf(stderr, "Can't read file header\n");
  461.         return(0);
  462.     }
  463.     
  464.     if (strncmp(temp + I_TYPEOFF, "SIT!", 4) == 0 &&
  465.         strncmp(temp + I_AUTHOFF, "SIT!", 4) == 0) {    /* MacBinary format */
  466.         fseek(infp, (long)(INFOBYTES-FILEHDRSIZE), 1);    /* Skip over header */
  467.     }
  468.     }
  469. }
  470.  
  471. /*
  472.   readfilehdr - reads the file header for each file and the folder start
  473.   and end records.
  474.  
  475.   returns: H_ERROR = error
  476.        H_EOF   = EOF
  477.        H_WRITE = write file/folder
  478.        H_SKIP  = skip file/folder
  479. */
  480.  
  481. readfilehdr(f, skip)
  482. struct fileHdr *f;
  483. int skip;
  484. {
  485.     unsigned short crc;
  486.     int i, n, write_it, isfolder;
  487.     char hdr[FILEHDRSIZE];
  488.     char ch, *mp, *up;
  489.     char *tp, temp[10];
  490.  
  491.     for (i = 0; i < INFOBYTES; i++)
  492.     info[i] = '\0';
  493.  
  494.     /* read in the next file header, which could be folder start/end record */
  495.     n = fread(hdr, 1, FILEHDRSIZE, infp);
  496.     if (n == 0)            /* return 0 on EOF */
  497.     return(H_EOF);
  498.     else if (n != FILEHDRSIZE) {
  499.     fprintf(stderr, "Can't read file header\n");
  500.     return(H_ERROR);
  501.     }
  502.  
  503.     /* check the CRC for the file header */
  504.     crc = INIT_CRC;
  505.     crc = updcrc(crc, hdr, FILEHDRSIZE - 2);
  506.     f->hdrCRC = get2(hdr + F_HDRCRC);
  507.     if (f->hdrCRC != crc) {
  508.     fprintf(stderr, "Header CRC mismatch: got 0x%04x, need 0x%04x\n",
  509.         f->hdrCRC, crc);
  510.     return(H_ERROR);
  511.     }
  512.  
  513.     /* grab the name of the file or folder */
  514.     n = hdr[F_FNAME] & BYTEMASK;
  515.     if (n > F_NAMELEN)
  516.     n = F_NAMELEN;
  517.     info[I_NAMEOFF] = n;
  518.     copy(info + I_NAMEOFF + 1, hdr + F_FNAME + 1, n);
  519.     strncpy(mname, hdr + F_FNAME + 1, n);
  520.     mname[n] = '\0';
  521.     /* copy to a string with no illegal Unix characters in the file name */
  522.     mp = mname;
  523.     up = uname;
  524.     while ((ch = *mp++) != '\0') {
  525.     if (ch <= ' ' || ch > '~' || index("/!()[]*<>?\\\"$\';&`", ch) != NULL)
  526.         ch = '_';
  527.     *up++ = ch;
  528.     }
  529.     *up = '\0';
  530.  
  531.     /* get lots of other stuff from the header */
  532.     f->compRMethod = hdr[F_COMPRMETHOD];
  533.     f->compDMethod = hdr[F_COMPDMETHOD];
  534.     f->rsrcLength = get4(hdr + F_RSRCLENGTH);
  535.     f->dataLength = get4(hdr + F_DATALENGTH);
  536.     f->compRLength = get4(hdr + F_COMPRLENGTH);
  537.     f->compDLength = get4(hdr + F_COMPDLENGTH);
  538.     f->rsrcCRC = get2(hdr + F_RSRCCRC);
  539.     f->dataCRC = get2(hdr + F_DATACRC);
  540.  
  541.     /* if it's an end folder record, don't need to do any more */
  542.     if (f->compRMethod == endFolder && f->compDMethod == endFolder)
  543.     return(H_WRITE);
  544.  
  545.     /* prepare an info file in case its needed */
  546.  
  547.     copy(info + I_TYPEOFF, hdr + F_FTYPE, 4);
  548.     copy(info + I_AUTHOFF, hdr + F_CREATOR, 4);
  549.     copy(info + I_FLAGOFF, hdr + F_FNDRFLAGS, 2);
  550. #ifdef INITED_BUG
  551.     info[INITED_OFF] &= INITED_MASK; /* reset init bit */
  552. #endif
  553.     copy(info + I_DLENOFF, hdr + F_DATALENGTH, 4);
  554.     copy(info + I_RLENOFF, hdr + F_RSRCLENGTH, 4);
  555.     copy(info + I_CTIMOFF, hdr + F_CREATIONDATE, 4);
  556.     copy(info + I_MTIMOFF, hdr + F_MODDATE, 4);
  557.  
  558.     isfolder = f->compRMethod == startFolder && f->compDMethod == startFolder;
  559.     
  560.     /* list the file name if verbose or listonly mode, also if query mode */
  561.     if (skip)            /* skip = 1 if skipping all in this folder */
  562.     write_it = 0;
  563.     else {
  564.     write_it = 1;
  565.     if (listonly || verbose || query) {
  566.         for (i = 0; i < depth; i++)
  567.         putchar(' ');
  568.         if (isfolder)
  569.         printf("Folder: \"%s\"", uname);
  570.         else
  571.         printf("name=\"%s\", type=%4.4s, author=%4.4s, data=%ld, rsrc=%ld",
  572.                uname, hdr + F_FTYPE, hdr + F_CREATOR,
  573.                f->dataLength, f->rsrcLength);
  574.         if (query) {    /* if querying, check with the boss */
  575.         printf(" ? ");
  576.         fgets(temp, sizeof(temp) - 1, stdin);
  577.         tp = temp;
  578.         write_it = 0;
  579.         while (*tp != '\0') {
  580.             if (*tp == 'y' || *tp == 'Y') {
  581.             write_it = 1;
  582.             break;
  583.             }
  584.             else
  585.             tp++;
  586.         }
  587.         }
  588.         else        /* otherwise, terminate the line */
  589.         putchar('\n');
  590.     }
  591.     }
  592.     return(write_it ? H_WRITE : H_SKIP);
  593. }
  594.  
  595. check_access(fname)    /* return 0 if OK to write on file fname, -1 otherwise */
  596. char *fname;
  597. {
  598.     char temp[10], *tp;
  599.  
  600.     if (access(fname, 0) == -1) {
  601.     return(0);
  602.     }
  603.     else {
  604.     printf("%s exists.  Overwrite? ", fname);
  605.     fgets(temp, sizeof(temp) - 1, stdin);
  606.     tp = temp;
  607.     while (*tp != '\0') {
  608.         if (*tp == 'y' || *tp == 'Y') {
  609.         return(0);
  610.         }
  611.         else
  612.         tp++;
  613.     }
  614.     }
  615.     return(-1);
  616. }
  617.  
  618. unsigned short write_file(fname, ibytes, obytes, type)
  619. char *fname;
  620. unsigned long ibytes, obytes;
  621. unsigned char type;
  622. {
  623.     unsigned short crc;
  624.     int i, n, ch, lastch;
  625.     FILE *outf;
  626.     char temp[256];
  627.  
  628.     crc = INIT_CRC;
  629.     chkcrc = 1;            /* usually can check the CRC */
  630.  
  631.     if (check_access(fname) == -1) {
  632.     fseek(infp, ibytes, 1);
  633.     chkcrc = 0;        /* inhibit crc check if file not written */
  634.         return(-1);
  635.     }
  636.     
  637.     switch (type) {
  638.       case noComp:        /* no compression */
  639.     outf = fopen(fname, "w");
  640.     if (outf == NULL) {
  641.         perror(fname);
  642.         exit(1);
  643.     }
  644.     while (ibytes > 0) {
  645.         n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
  646.         n = fread(iobuf, 1, n, infp);
  647.         if (n == 0)
  648.         break;
  649.         crc = updcrc(crc, iobuf, n);
  650.         outc(iobuf, n, outf);
  651.         ibytes -= n;
  652.     }
  653.     fclose(outf);
  654.     break;
  655.       case rleComp:        /* run length encoding */
  656.     outf = fopen(fname, "w");
  657.     if (outf == NULL) {
  658.         perror(fname);
  659.         exit(1);
  660.     }
  661.     while (ibytes > 0) {
  662.         ch = getc(infp) & 0xff;
  663.         ibytes--;
  664.         if (ch == 0x90) {    /* see if its the repeat marker */
  665.         n = getc(infp) & 0xff; /* get the repeat count */
  666.         ibytes--;
  667.         if (n == 0) { /* 0x90 was really an 0x90 */
  668.             iobuf[0] = 0x90;
  669.             crc = updcrc(crc, iobuf, 1);
  670.             outc(iobuf, 1, outf);
  671.         }
  672.         else {
  673.             n--;
  674.             for (i = 0; i < n; i++)
  675.             iobuf[i] = lastch;
  676.             crc = updcrc(crc, iobuf, n);
  677.             outc(iobuf, n, outf);
  678.         }
  679.         }
  680.         else {
  681.         iobuf[0] = ch;
  682.         crc = updcrc(crc, iobuf, 1);
  683.         lastch = ch;
  684.         outc(iobuf, 1, outf);
  685.         }
  686.     }
  687.     fclose(outf);
  688.     break;
  689.       case lzwComp:        /* LZW compression */
  690.     sprintf(temp, "%s%s", COMPRESS, " -d -c -n -b 14 ");
  691.     if (txtmode) {
  692.         strcat(temp, "| tr \'\\015\' \'\\012\' ");
  693.         chkcrc = 0;        /* can't check CRC in this case */
  694.     }
  695.     strcat(temp, "> '");
  696.     strcat(temp, fname);
  697.     strcat(temp, "'");
  698.     outf = popen(temp, "w");
  699.     if (outf == NULL) {
  700.         perror(fname);
  701.         exit(1);
  702.     }
  703.     while (ibytes > 0) {
  704.         n = (ibytes > IOBUFSIZ) ? IOBUFSIZ : ibytes;
  705.         n = fread(iobuf, 1, n, infp);
  706.         if (n == 0)
  707.         break;
  708.         fwrite(iobuf, 1, n, outf);
  709.         ibytes -= n;
  710.     }
  711.     pclose(outf);
  712.     if (chkcrc) {
  713.         outf = fopen(fname, "r"); /* read the file to get CRC value */
  714.         if (outf == NULL) {
  715.         perror(fname);
  716.         exit(1);
  717.         }
  718.         while (1) {
  719.         n = fread(iobuf, 1, IOBUFSIZ, outf);
  720.         if (n == 0)
  721.             break;
  722.         crc = updcrc(crc, iobuf, n);
  723.         }
  724.         fclose(outf);
  725.     }
  726.     break;
  727.       case hufComp:        /* Huffman compression */
  728.     outf = fopen(fname, "w");
  729.     if (outf == NULL) {
  730.         perror(fname);
  731.         exit(1);
  732.     }
  733.     nodeptr = nodelist;
  734.     bit = 0;        /* put us on a byte boundary */
  735.     read_tree();
  736.     while (obytes > 0) {
  737.         n = (obytes > IOBUFSIZ) ? IOBUFSIZ : obytes;
  738.         for (i = 0; i < n; i++)
  739.         iobuf[i] = gethuffbyte(DECODE);
  740.         crc = updcrc(crc, iobuf, n);
  741.         outc(iobuf, n, outf);
  742.         obytes -= n;
  743.     }
  744.     fclose(outf);
  745.     break;
  746.       default:
  747.     fprintf(stderr, "Unknown compression method\n");
  748.     chkcrc = 0;        /* inhibit crc check if file not written */
  749.     return(-1);
  750.     }
  751.  
  752.     return(crc & 0xffff);
  753. }
  754.  
  755. outc(p, n, fp)
  756. char *p;
  757. int n;
  758. FILE *fp;
  759. {
  760.     register char *p1;
  761.     register int i;
  762.     if (txtmode) {
  763.     for (i = 0, p1 = p; i < n; i++, p1++)
  764.         if ((*p1 & BYTEMASK) == '\r')
  765.         *p1 = '\n';
  766.     }
  767.     fwrite(p, 1, n, fp);
  768. }
  769.  
  770. long get4(bp)
  771. char *bp;
  772. {
  773.     register int i;
  774.     long value = 0;
  775.  
  776.     for (i = 0; i < 4; i++) {
  777.     value <<= 8;
  778.     value |= (*bp & BYTEMASK);
  779.     bp++;
  780.     }
  781.     return(value);
  782. }
  783.  
  784. short get2(bp)
  785. char *bp;
  786. {
  787.     register int i;
  788.     int value = 0;
  789.  
  790.     for (i = 0; i < 2; i++) {
  791.     value <<= 8;
  792.     value |= (*bp & BYTEMASK);
  793.     bp++;
  794.     }
  795.     return(value);
  796. }
  797.  
  798. copy(p1, p2, n)
  799. char *p1, *p2;
  800. int n;
  801. {
  802.     while (n-- > 0)
  803.         *p1++ = *p2++;
  804. }
  805.  
  806. /* This routine recursively reads the Huffman encoding table and builds
  807.    and decoding tree. */
  808.  
  809. struct node *read_tree()
  810. {
  811.     struct node *np;
  812.     np = nodeptr++;
  813.     if (getbit() == 1) {
  814.         np->flag = 1;
  815.         np->byte = gethuffbyte(NODECODE);
  816.     }
  817.     else {
  818.         np->flag = 0;
  819.         np->zero = read_tree();
  820.         np->one  = read_tree();
  821.     }
  822.     return(np);
  823. }
  824.  
  825. /* This routine returns the next bit in the input stream (MSB first) */
  826.  
  827. getbit()
  828. {
  829.     static char b;
  830.     if (bit == 0) {
  831.         b = getc(infp) & 0xff;
  832.         bit = 8;
  833.     }
  834.     bit--;
  835.     return((b >> bit) & 1);
  836. }
  837.  
  838. /* This routine returns the next 8 bits.  If decoding is on, it finds the
  839. byte in the decoding tree based on the bits from the input stream.  If
  840. decoding is not on, it either gets it directly from the input stream or
  841. puts it together from 8 calls to getbit(), depending on whether or not we
  842. are currently on a byte boundary
  843. */
  844. gethuffbyte(decode)
  845. int decode;
  846. {
  847.     register struct node *np;
  848.     register int i, b;
  849.     if (decode == DECODE) {
  850.         np = nodelist;
  851.         while (np->flag == 0)
  852.             np = (getbit()) ? np->one : np->zero;
  853.         b = np->byte;
  854.     }
  855.     else {
  856.         if (bit == 0)    /* on byte boundary? */
  857.             b = getc(infp) & 0xff;
  858.         else {        /* no, put a byte together */
  859.             b = 0;
  860.             for (i = 8; i > 0; i--) {
  861.                 b = (b << 1) + getbit();
  862.             }
  863.         }
  864.     }
  865.     return(b);
  866. }
  867.