home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / unpitsou.pit / unpit.c.pit / unpit.c next >
Encoding:
C/C++ Source or Header  |  1986-10-01  |  48.6 KB  |  1,606 lines

  1. /*
  2.  
  3.         unpit - Macintosh PackIt file packer/unpacker
  4.         
  5. This program is a Macintosh translation/expansion of 'unpit', a Unix program
  6. written by Allan G. Weber (Weber%Brand@USC-ECL) that unpacks Packit-I and -II
  7. files.  Why port it to the Mac?  Well, Packit-II is not free, but many people
  8. are posting {public domain, noncommercial, or shareware} programs to USENET's
  9. net.sources.mac and various other places in Packit-II format, apparently due to
  10. the popularity achieved by the free Packit-I.  This program is intended to get
  11. rid of the need to choose between these undesirable alternatives:
  12.     (1) losing access to freely-distributed software that's in Packit II format
  13.     (2) uploading Packit-II files to a Unix box just for the purpose of running
  14.         unpit on them (unpit is great if the files are already on the Unix box,
  15.         but what does one do with files that come from, say, GENIE?)
  16.     (3) forking over $10 not because one wants to use Packit-II but because one
  17.         can't put up with (1) or (2), and has a sense of ethics that prevents:
  18.     (4) using Packit-II to decode downloaded files without paying for it
  19.  
  20. Translation by Thomas D. Newton (ARPA address: Thomas.Newton@spice.cs.cmu.edu)
  21.  
  22. Copyright Information:
  23.     By now, this program has a little bit of everything in it.  I would assume
  24.     that the Unix version is (C) 1986 Allan Weber, except for the pieces taken
  25.     from macput.c and macget.c.  I suspect that this version doesn't have much
  26.     of the macget/macput code left (when on a Mac, it makes sense just to call
  27.     the filesystem rather than to write an .INFO file).  On the other hand, it
  28.     does have some code taken from BINConvert ((C) 1986 Thomas D. Newton), and
  29.     some code taken from SKEL, and some code from standard CS textbooks.  As I
  30.     am writing this using LightSpeed C, the executable version will have a (C)
  31.     Think notice.  Taking everything into account, this version of Unpit is
  32.  
  33.         Copyright (C) 1986 Thomas D. Newton
  34.         This program may be copied and used for noncommercial purposes.
  35.             It may not be sold or offered as inducement to buy a product.
  36.         Parts of this program are derived from code written by various
  37.             authors which itself can be copied and used noncommercially.
  38.  
  39. Compilation Information:
  40.     This program was compiled using LightSpeed C (tm) on a Macintosh.  It uses
  41.     the "MacTraps" library, and the resources generated by running RMaker upon
  42.     the companion file 'unpit.R'.
  43.  
  44. Modification History:
  45.     Version     Date     Who/Comments
  46.     =======    ======    ============
  47.       0.1      Aug 86    Thomas Newton (tdn@spice.cs.cmu.edu)
  48.                    Created.  Translated Unix 'unpit' to Macintosh;
  49.                    added SKEL-like user interface shell, Packit-I
  50.                    packing, delete file, and help commands; hacked
  51.                    up the structure of the program to gain speed.
  52.  
  53.       0.2      Aug 86    Thomas Newton (tdn@spice.cs.cmu.edu)
  54.                    Added code to call Disk Initialization Package
  55.                    so blank disks don't act "dead" when inserted.
  56.  
  57.                    Removed CLOVER-B as an alias for EDIT'CLEAR --
  58.                    it doesn't seem to work with MockWrite, and it
  59.                    seems that keyboard events that happen while a
  60.                    DA window is active are not returned to Unpit,
  61.                    so Unpit can't "force" the alias to work.
  62.                    
  63.                    Fixed a few calls to Err() that didn't include
  64.                    the second parameter; changed main() to put up
  65.                    a dialog box rather than beeping if user tries
  66.                    to Print packed files from the Finder.
  67.  
  68.       0.3      Sep 86    Thomas Newton (tdn@spice.cs.cmu.edu)
  69.                    Added the ability to produce compressed packed
  70.                    files, using Huffman compression (Knuth's "The
  71.                    Art of Computer Programming", Volume One, Page
  72.                    402 describes how to build weighted trees with
  73.                    minimum total weight using Huffman's method).
  74.                    
  75.                    Reduced the amount of text in the "About" box,
  76.                    both to get the size of the executable version
  77.                    down and because Unpit is now roughly equal to
  78.                    PackIt-II, rather than to PackIt-1.5 (not that
  79.              there ever was a Packit-1.5 . . .).
  80. -------------------------------------------------------------------------------
  81.  
  82. Format of a Packit file:
  83.  
  84. Repeat the following sequence for each file in the Packit file:
  85.  
  86.     4 byte identifier ("PMag" = not compressed, "Pma4" = compressed)
  87.     variable length compression data (if compressed file)
  88.     92 byte header (see struct pit_header below) *
  89.     2 bytes CRC number *
  90.     data fork (length from header) *
  91.     resource fork (length from header) *
  92.     2 bytes CRC number *
  93.  
  94. Last file is followed by the 4 byte Ascii string, "Pend", and then the EOF.
  95.  
  96. * these are in compressed form if compression is on for the file
  97.  
  98. */
  99.  
  100. #include <DialogMgr.h>
  101. #include <EventMgr.h>
  102. #include <FileMgr.h>
  103. #include <MacTypes.h>
  104. #include <MemoryMgr.h>
  105. #include <MenuMgr.h>
  106. #include <pascal.h>
  107. #include <Quickdraw.h>
  108. #include <SegmentLdr.h>
  109. #include <StdFilePkg.h>
  110. #include <WindowMgr.h>
  111.  
  112. typedef char byte;
  113. typedef int  word;
  114.  
  115. struct pit_header {    /* Packit file header (92 bytes)
  116.     byte nlen;    /* number of characters in packed file name */
  117.     byte name[63];    /* name of packed file */
  118.     byte type[4];    /* file type */
  119.     byte auth[4];    /* file creator */
  120.     word flags;    /* Finder flags */
  121.     word lock;    /* low-order bit seems to be lock; others are ??? */
  122.     long dlen;    /* number of bytes in data fork */
  123.     long rlen;    /* number of bytes in resource fork */
  124.     long ctim;    /* file creation time */
  125.     long mtim;    /* file modified time */
  126. };
  127.  
  128. #define HDRBYTES  92
  129.  
  130. #define BYTEMASK 0xff
  131.  
  132. #define H_NAMELEN 63
  133.  
  134. #define H_NLENOFF 0
  135. #define H_NAMEOFF 1
  136. #define H_TYPEOFF 64
  137. #define H_AUTHOFF 68
  138. #define H_FLAGOFF 72
  139. #define H_LOCKOFF 74
  140. #define H_DLENOFF 76
  141. #define H_RLENOFF 80
  142. #define H_CTIMOFF 84
  143. #define H_MTIMOFF 88
  144.  
  145. #define INITED 0x0100
  146. #define FLOCK  0x01
  147.  
  148. /* Screen locations of SFGetFile and SFPutFile boxes */
  149. #define GET_FILE_X  82
  150. #define GET_FILE_Y  92
  151. #define PUT_FILE_X 104
  152. #define PUT_FILE_Y  92
  153.  
  154. /* Screen location of disk initialization dialog */
  155. #define INIT_X     112
  156. #define INIT_Y      80
  157.  
  158.  
  159. typedef struct nod {
  160.     int flag, byte, index2;
  161.     long count;
  162.     struct nod *one, *zero;
  163. } node, *nodeptr;
  164.  
  165. node    nodelist[512];            /* 512 should be big enough */
  166. nodeptr heap[513], freenode;
  167.  
  168. struct {
  169.     long bits;
  170.     int bcount;
  171. } byte_code[256];
  172.  
  173. unsigned char hdr[HDRBYTES];
  174.  
  175. typedef int crctype;
  176.  
  177. long datalen, rsrclen;
  178. int decode, bit;
  179.  
  180. Boolean Filter;        /* if TRUE, Unpack... shows only PIT and TEXT files */
  181. Boolean Compress;       /* if TRUE, Pack...   uses Huffman compression      */
  182.  
  183. /* forward declarations */
  184. nodeptr        read_tree();
  185. long        get4();
  186. crctype        read_hdr();
  187. crctype        write_fork();
  188. crctype        getcrc();
  189. Boolean        pack_files();
  190.  
  191. /*
  192.  * Error codes that can be used as the second parameter to Err (a function
  193.  * declared below).  They let Err() know roughly what the program was doing
  194.  * at the time that an error occurred so that it can display somewhat more
  195.  * informative error messages.
  196.  */
  197.  
  198. enum {
  199.     errCreateFile,    /* creating output file */
  200.     errOpenDataFk,    /* opening data fork for write */
  201.     errOpenRsrcFk,    /* opening resource fork for write */
  202.     errGetFInfo,    /* getting information for unpacked file */
  203.     errSetFInfo,    /* setting information for unpacked file */
  204.     errWriting,        /* writing output file */
  205.     errReading,        /* reading input file */
  206.     errLock,        /* locking unpacked file */
  207.     errDelete        /* deleting file */
  208.     };
  209.     
  210. /*
  211. Report an error to the user.  This function is used to process the return
  212. codes received from FSOpen and the like.
  213. ############################       Err     ##############################
  214. */
  215. Boolean Err(theErr, activity)
  216. OSErr theErr;
  217. int activity;
  218. {   char *errText, *errWhy;
  219.  
  220.     if (theErr == noErr)
  221.         return FALSE;
  222.     else {
  223.         switch (activity) {
  224.             case errCreateFile:
  225.                 errWhy = "\Pcreating output file"; break;
  226.             case errOpenDataFk:
  227.                 errWhy = "\Popening data fork"; break;
  228.             case errOpenRsrcFk:
  229.                 errWhy = "\Popening resource fork"; break;
  230.             case errGetFInfo:
  231.                 errWhy = "\Pgetting Finder information"; break;
  232.             case errSetFInfo:
  233.                 errWhy = "\Psetting Finder information"; break;
  234.             case errWriting:
  235.                 errWhy = "\Pwriting to output file"; break;
  236.             case errReading:
  237.                 errWhy = "\Preading from Packit file"; break;
  238.             case errLock:
  239.                 errWhy = "\Plocking unpacked file"; break;
  240.             case errDelete:
  241.                 errWhy = "\Ptrying to delete file"; break;
  242.             default:
  243.                 errWhy = "\P<doing random activity>"; break;
  244.             }
  245.             
  246.         switch (theErr) {
  247.         case bdNamErr:    errText = "\PBad file name"; break;
  248.             case dupFNErr:    errText = "\PDuplicate file name"; break;
  249.         case dirFulErr:    errText = "\PDirectory full"; break;
  250.         case extFSErr:    errText = "\PExternal file system"; break;
  251.         case ioErr:        errText = "\PDisk I/O error"; break;
  252.         case nsvErr:    errText = "\PNo such volume"; break;
  253.         case vLckdErr:    errText = "\PSoftware volume lock"; break;
  254.         case wPrErr:    errText = "\PHardware volume lock"; break;
  255.         case mFulErr:    errText = "\PMemory full"; break;
  256.         case tmfoErr:    errText = "\PToo many files open"; break;
  257.         case dskFulErr:    errText = "\PDisk full"; break;
  258.         case fLckdErr:    errText = "\PFile locked"; break;
  259.         case fnOpnErr:    errText = "\PFile not open"; break;
  260.         case opWrErr:    errText = "\PFile already open for writing"; break;
  261.         case wrPermErr:    errText = "\PPermission doesn't allow writing"; break;
  262.             default:            errText = "\P<unanticipated error>"; break;
  263.         }
  264.         
  265. #define alertboxid 258
  266.     ParamText(errWhy,errText,0L,0L);
  267.     StopAlert(alertboxid, 0L);
  268.         return TRUE;
  269.         }
  270. }
  271.  
  272. /*
  273.  * Error codes that can be used as parameters to Err2 (below)
  274.  */
  275.  
  276. enum {
  277.     errHeaderCRC,    /* file header CRC mismatch */
  278.     errFileCRC,        /* file data/rsrc CRC mismatch */
  279.     errNotPackit,    /* unrecognized PACKIT format */
  280.     errBadPrint,        /* attempt to Print files from Finder */
  281.     errCompress         /* internal error: out of room for compression */
  282.     }
  283.  
  284. /*
  285. Report an error to the user.  This function is used for error messages
  286. that aren't directly caused by I/O errors
  287. ############################      Err2     ##############################
  288. */
  289.  
  290. Err2(theErr)
  291. int theErr;
  292. {
  293.     switch (theErr) {
  294.         case errHeaderCRC:
  295.             ParamText("\PError: file header CRC mismatch",0L,0L,0L);
  296.             break;
  297.         case errFileCRC:
  298.             ParamText("\PError: file data/resource CRC mismatch",0L,0L,0L);
  299.             break;
  300.         case errNotPackit:
  301.             ParamText("\PThis is not a Packit-I or Packit-II file.",0L,0L,0L);
  302.             break;
  303.         case errBadPrint:
  304.             ParamText("\PPrinting packed files is not supported.",0L,0L,0L);
  305.             break;
  306.         case errCompress:
  307.             ParamText("\POut of bits for character codes.",0L,0L,0L);
  308.             break;
  309.     }
  310.     StopAlert(alertboxid+1,0L);
  311. }
  312.  
  313. /*
  314. Open a file's resource fork
  315. ##############################   Open_Rsrc   ##############################
  316. */
  317. Open_Rsrc(fName, vRefNum, perm, ref_num)
  318. StringPtr fName;
  319. int       vRefNum, perm;
  320. int       *ref_num;
  321. {   ParamBlockRec ParamB;
  322.     int ret_val;
  323.  
  324.     ParamB.ioParam.ioCompletion = 0L;
  325.     ParamB.ioParam.ioNamePtr = fName;
  326.     ParamB.ioParam.ioVRefNum = vRefNum;
  327.     ParamB.ioParam.ioVersNum = 0;
  328.     ParamB.ioParam.ioPermssn = perm;
  329.     ParamB.ioParam.ioMisc = 0L;
  330.     ret_val = PBOpenRF(&ParamB,0);
  331.     *ref_num = ParamB.ioParam.ioRefNum;
  332.     return ret_val;
  333. }
  334.  
  335. /*
  336. Compare two strings that are four bytes long.  This allows us to toss out
  337. the Strings library, since Unpit was pulling it in just for strncmp . . .
  338. ##############################   Str_Cmp_4   ##############################
  339. */
  340. Boolean str_cmp_4(str1, str2)
  341. char *str1, *str2;
  342. {   int i;
  343.     for (i = 0; i < 4; i++)
  344.         if (str1[i] != str2[i]) return FALSE;
  345.     return TRUE;
  346. }
  347.  
  348. /*
  349. Read a byte from the file denoted by pit_ref_num, using buffered reads.
  350. Note: this routine is bypassed for speed when unpacking Packit-I files.
  351. ##############################   Read_Byte   ##############################
  352. */
  353. #define BSIZE 4096
  354. int pit_ref_num;            /* Mac filesystem reference number */
  355. long pit_bytes_left;            /* # of bytes to be read from disk */
  356. unsigned char pit_buffer[BSIZE];    /* Buffer for bytes read from disk */
  357. int pit_index, pit_max_index;        /* For tracking the buffer's state */
  358.  
  359. #define read_byte(ARG1) \
  360.     ((pit_index < pit_max_index) ? pit_buffer[pit_index++] : do_read(ARG1))
  361.  
  362. do_read(eflag)
  363. Boolean *eflag;
  364. {   long Count;
  365.     unsigned char Ch;
  366.  
  367.     if (pit_index < pit_max_index)
  368.         return pit_buffer[pit_index++];
  369.     else
  370.         if (pit_bytes_left == 0L) {
  371.             Err(ioErr,errReading);
  372.             *eflag = TRUE;
  373.             return 0;
  374.             }
  375.         else {
  376.         Count = (pit_bytes_left < BSIZE) ? pit_bytes_left : BSIZE;
  377.         pit_index = 0; pit_max_index = (int) Count; pit_bytes_left -= Count;
  378.         if (Err(FSRead(pit_ref_num, &Count, &pit_buffer), errReading)) {
  379.             *eflag = TRUE;
  380.             return 0;
  381.         }
  382.         else
  383.             return pit_buffer[pit_index++];
  384.     }
  385. }
  386.  
  387. /*
  388. Update the value of CRC to include the bytes in a random chunk of memory.
  389. ###############################   Upd_Crc   ###############################
  390. */
  391. crctype upd_crc(crc, theBytes, byteCount)
  392. register crctype crc;
  393. unsigned char   *theBytes;
  394. long             byteCount;
  395. {    register int  i;
  396.     register long n;
  397.     
  398.     for (n = 0; n < byteCount; n++) {
  399.         crc = crc ^ ((int)theBytes[n] << 8);
  400.         for (i = 0; i < 8; i++)
  401.             if (crc & 0x8000)
  402.                 crc = (crc << 1) ^ 0x1021;
  403.             else
  404.                 crc <<= 1;
  405.     }
  406.     return(crc & 0xffff);
  407. }
  408.  
  409. /*
  410. Put up a dialog box that allows the user to select a file to unpack.
  411. ################################   UnPack   ###############################
  412. */
  413. unpack()
  414. {   SFReply PitRecord;
  415.     SFTypeList PitTypes;
  416.     Point where;
  417.     ParamBlockRec ParamB;
  418.     
  419.     where.h = GET_FILE_X; where.v = GET_FILE_Y;
  420.     PitTypes[0] = 'PIT '; PitTypes[1] = 'TEXT';
  421.     SFGetFile(where, "", 0L, Filter ? 2 : -1, PitTypes, 0L, &PitRecord);
  422.     if (PitRecord.good) {
  423.         ParamB.fileParam.ioCompletion = 0L;
  424.         ParamB.fileParam.ioNamePtr = PitRecord.fName;
  425.         ParamB.fileParam.ioVRefNum = PitRecord.vRefNum;
  426.         ParamB.fileParam.ioFVersNum = 0;
  427.         ParamB.fileParam.ioFDirIndex = 0;    /* use NamePtr/VRefNum */
  428.         PBGetFInfo(&ParamB,0);
  429.         pit_bytes_left = ParamB.fileParam.ioFlLgLen;
  430.         if (pit_bytes_left < 4L)
  431.             Err2(errNotPackit);   /* more meaningful than "I/O Error..." */
  432.         else {
  433.             pit_index = 0; pit_max_index = -1;
  434.             FSOpen(PitRecord.fName,PitRecord.vRefNum,&pit_ref_num);
  435.             unpit();
  436.             FSClose(pit_ref_num);
  437.         }
  438.     }
  439. }
  440.  
  441. /*
  442. Put up a dialog box that allows the user to delete a file.
  443. ################################   Delete   ###############################
  444. */
  445. #define deleteconfirm 260
  446. delete()
  447. {   SFReply DelRecord;
  448.     Point where;
  449.  
  450.     where.h = GET_FILE_X; where.v = GET_FILE_Y;
  451.     SFGetFile(where, "", 0L, -1, 0L, 0L, &DelRecord);
  452.     if (DelRecord.good) {
  453.         ParamText(DelRecord.fName, 0L, 0L, 0L);
  454.         if (CautionAlert(deleteconfirm, 0L) == 1)    /* the OK button */
  455.             Err(FSDelete(DelRecord.fName, DelRecord.vRefNum),errDelete);
  456.     }
  457. }
  458.  
  459.  
  460. /*
  461. Unpack a Packit-I or Packit-II files into its component files.
  462. ################################   UnPit   #################################
  463.  
  464.     This routine assumes that the input file has already been opened and
  465.     that the input file can be read one byte at a time using read_byte().
  466.     Errors that occur during the attempted unpacking will (hopefully) be
  467.     reported to the user within UnPit or functions it calls; in any case,
  468.     the caller should close the input file when UnPit returns.
  469.  
  470. */
  471. #define statusid 263
  472. unpit()
  473. {   char temp[4];        /* used when searching for Packit identifier */
  474.     SFReply OutRecord;        /* names and volume numbers of output files  */
  475.     Point where;        /* where to locate the SFPutFile dialog box  */
  476.     Boolean saveOutput;        /* to allow extracting pieces of a .PIT file */
  477.     ParamBlockRec ParamB;    /* needed to make low-level filesystem calls */
  478.     int out_ref_num, i;
  479.     crctype data_crc, crc;
  480.     Boolean eflag = FALSE;
  481.     DialogPtr theDialog = 0L;
  482.  
  483.     while(1) {
  484.         for (i = 0; i < 4; i++) {        /* PACKIT signature */
  485.             temp[i] = read_byte(&eflag);
  486.             if (eflag) return;
  487.             }
  488.     if (str_cmp_4(temp, "PMag") || str_cmp_4(temp, "PMa4")) {
  489.         if (temp[3] == '4') {
  490.         freenode = nodelist;
  491.         read_tree(&eflag); if (eflag) return;
  492.         decode = 1;
  493.         }
  494.         else
  495.             decode = 0;
  496.         data_crc = read_hdr(&eflag); if (eflag) return;
  497.         crc = getcrc(&eflag); if (eflag) return;
  498.         if (crc != data_crc) {
  499.                 Err2(errHeaderCRC);
  500.             return;
  501.         }
  502.         
  503.         /* Get name of output file */
  504.         where.h = PUT_FILE_X; where.v = PUT_FILE_Y;
  505.         SFPutFile(where, "\PSave as:", &hdr[H_NLENOFF], 0L, &OutRecord);
  506.         if (OutRecord.good) {
  507.             if ((i = Create(OutRecord.fName,OutRecord.vRefNum,'????','????'))
  508.                 == dupFNErr) { /* user said it was OK to replace...delete! */
  509.                 FSDelete(OutRecord.fName, OutRecord.vRefNum);
  510.                 i = Create(OutRecord.fName,OutRecord.vRefNum,'????','????');
  511.             }
  512.             if (Err(i,errCreateFile)) return;
  513.             saveOutput = TRUE;
  514.             if (Err(FSOpen(OutRecord.fName,OutRecord.vRefNum,&out_ref_num),
  515.             errOpenDataFk)) return;
  516.             }
  517.         else {
  518.             saveOutput = FALSE;
  519.             out_ref_num = 0;
  520.         }
  521.         
  522.         /* Give visual indication that file is being unpacked */
  523.         theDialog = GetNewDialog(statusid, 0L, -1L);
  524.         ParamText(0L,0L,saveOutput ? "\PUnpack":"\PSkipp",OutRecord.fName);
  525.         DrawDialog(theDialog);
  526.  
  527.         /* First, take care of writing the file's data fork */
  528.         data_crc = write_fork(saveOutput, out_ref_num, datalen,
  529.                       (crctype) 0, &eflag);
  530.         if (saveOutput) FSClose(out_ref_num);
  531.         if (eflag) goto error;
  532.         
  533.         /* Next, take care of writing the file's resource fork. */
  534.         if (saveOutput && (rsrclen > 0)) {
  535.             if (Err(Open_Rsrc(OutRecord.fName,OutRecord.vRefNum,fsWrPerm,
  536.                     &out_ref_num), errOpenRsrcFk)) goto error;
  537.             }
  538.         else
  539.             out_ref_num = 0;
  540.         data_crc = write_fork(saveOutput, out_ref_num, rsrclen,
  541.                       data_crc, &eflag);
  542.         if (saveOutput) FSClose(out_ref_num);
  543.         if (eflag) goto error;
  544.  
  545.         /* Check the CRC to make sure the file was unpacked intact */
  546.         crc = getcrc(&eflag);
  547.         if (eflag || (crc != data_crc)) {
  548.         Err2(errFileCRC);
  549.         if (saveOutput) FSDelete(OutRecord.fName, OutRecord.vRefNum);
  550.         goto error;
  551.         }
  552.  
  553.         /* Finally, set the filesystem and Finder info for the file */
  554.         if (saveOutput) {
  555.         ParamB.fileParam.ioCompletion = 0L;
  556.         ParamB.fileParam.ioNamePtr = OutRecord.fName;
  557.         ParamB.fileParam.ioVRefNum = OutRecord.vRefNum;
  558.         ParamB.fileParam.ioFVersNum = 0;
  559.         ParamB.fileParam.ioFDirIndex = 0;    /* use NamePtr/VRefNum */
  560.         if (Err(PBGetFInfo(&ParamB,0),errGetFInfo)) goto error;
  561.  
  562.         ParamB.fileParam.ioFlFndrInfo.fdType = get4(&hdr[H_TYPEOFF]);
  563.         ParamB.fileParam.ioFlFndrInfo.fdCreator = get4(&hdr[H_AUTHOFF]);
  564.         ParamB.fileParam.ioFlFndrInfo.fdFlags =
  565.             (hdr[H_FLAGOFF] << 8) | hdr[H_FLAGOFF+1];
  566.         ParamB.fileParam.ioFlCrDat = get4(&hdr[H_CTIMOFF]);
  567.         ParamB.fileParam.ioFlMdDat = get4(&hdr[H_MTIMOFF]);
  568.  
  569.         /* turn INITed flag off since Finder hasn't seen file yet */
  570.         ParamB.fileParam.ioFlFndrInfo.fdFlags &= (0xFFFF ^ INITED);
  571.         if (Err(PBSetFInfo(&ParamB,0),errSetFInfo)) goto error;
  572.  
  573.         /* lock file if Packit header says to do so */
  574.         if (hdr[H_LOCKOFF+1] & 1)    /* don't look at other bits */
  575.             if (Err(SetFLock(OutRecord.fName,OutRecord.vRefNum),errLock))
  576.                 goto error;
  577.  
  578.         /* finally, flush volume to make sure that data is safe */
  579.         FlushVol(0L,OutRecord.vRefNum);
  580.         }
  581.         DisposDialog(theDialog); theDialog = 0L;
  582.         decode = 0;
  583.         bit = 0;    /* flush unused bits */
  584.         }
  585.     else if (str_cmp_4(temp, "PEnd"))
  586.         break;
  587.     else {
  588.         Err2(errNotPackit);
  589.         return;
  590.         }
  591.     }
  592. error:                /* should never be reached except by "goto" */
  593.     if (theDialog != 0L)
  594.         DisposDialog(theDialog);
  595.     return;
  596. }
  597.  
  598. /*
  599. Read compression decoding data.
  600. ##############################   Read_Tree   ##############################
  601.  
  602.     This routine recursively reads the compression decoding data.
  603.     It appears to be Huffman compression.
  604.     
  605. */
  606.  
  607. nodeptr read_tree(eflag)
  608. Boolean *eflag;
  609. {    int bit;
  610.  
  611.     nodeptr np;
  612.     np = freenode++;
  613.     bit = getbit(eflag); if (*eflag) return (0L);
  614.     if (bit == 1) {
  615.         np->flag = 1;
  616.         np->byte = getbyte(eflag); if (*eflag) return (0L);
  617.     }
  618.     else {
  619.         np->flag = 0;
  620.         np->zero = read_tree(eflag); if (*eflag) return (0L);
  621.         np->one  = read_tree(eflag); if (*eflag) return (0L);
  622.     }
  623.     return(np);
  624. }
  625.  
  626.  
  627. /*
  628. Read header containing information about the packed file to follow.
  629. ###############################   Read_Hdr   ##############################
  630. */
  631.  
  632. crctype read_hdr(eflag)
  633. Boolean *eflag;
  634. {
  635.     register int n;
  636.  
  637.     for (n = 0; n < HDRBYTES; n++) {
  638.         hdr[n] = getbyte(eflag);
  639.         if (*eflag) return;
  640.     }
  641.     datalen = get4(hdr + H_DLENOFF);
  642.     rsrclen = get4(hdr + H_RLENOFF);
  643.     return(upd_crc(0, hdr, (long) HDRBYTES));
  644. }
  645.  
  646.  
  647. /*
  648. Write one fork of an unpacked file.
  649. ##############################   Write_Fork   #############################
  650.  
  651.     saveOutput indicates whether or not the output should be saved; to skip
  652.         a file it is still necessary to read it from the .PIT file in order
  653.         to advance to the start of the next file that can be unpacked.
  654.  
  655. */
  656.  
  657. crctype write_fork(saveOutput, out_ref_num, bytes, crc, eflag)
  658. Boolean saveOutput, *eflag;
  659. int out_ref_num;
  660. long bytes;
  661. register crctype crc;
  662. {
  663.     register int b, i;
  664.     long Count;
  665.     static char out_buffer[BSIZE];
  666.     int out_index = 0;
  667.     
  668.     if ((decode == 0) && (bit == 0)) {
  669.         /* Special-case the unpacking of Packit-I files, as unpacking */
  670.         /* them using more general-purpose code takes >50% longer (33 */
  671.         /* vs 19 seconds when unpacking ResEdit on a RAMDisk).  While */
  672.         /* this code isn't very pretty, it gets the job done. . .     */
  673.  
  674.         if ((bytes > 0) && ((Count = pit_max_index-pit_index) > 0L)) {
  675.             /* we won't be using read_byte(), so empty its buffer */
  676.             if (bytes < Count) Count = bytes;
  677.             if (saveOutput && Err(FSWrite(out_ref_num, &Count,
  678.                 &pit_buffer[pit_index]),errWriting)) {
  679.                 *eflag = TRUE; return 0;
  680.             }
  681.             crc = upd_crc(crc, &pit_buffer[pit_index], Count);
  682.             bytes -= Count; pit_index += (int) Count;
  683.         }
  684.  
  685.         while (bytes > 0L) {
  686.             Count = (bytes >= BSIZE) ? BSIZE : bytes;
  687.             if (Err(FSRead (pit_ref_num,&Count,out_buffer),errReading) ||
  688.                 (saveOutput && Err( /* this isn't the most elegant code */
  689.                 FSWrite(out_ref_num,&Count,out_buffer),errWriting))) {
  690.                 *eflag = TRUE;
  691.                 return 0;
  692.             }
  693.             crc = upd_crc(crc, out_buffer, Count);
  694.             bytes -= Count; pit_bytes_left -= Count;
  695.         }
  696.     } else {
  697.         while (bytes-- > 0) {
  698.         b = getbyte(eflag); if (*eflag) return;
  699.         crc = crc ^ (b << 8);
  700.         for (i = 0; i < 8; i++)
  701.             if (crc & 0x8000)
  702.                 crc = (crc << 1) ^ 0x1021;
  703.             else
  704.                 crc <<= 1;
  705.         if (saveOutput) {
  706.             if (out_index == BSIZE) {
  707.             Count = (long) out_index;
  708.             if (Err(FSWrite(out_ref_num, &Count, &out_buffer),
  709.                 errWriting)) {
  710.                 *eflag = TRUE;
  711.                 return 0;
  712.             }
  713.             out_index = 0;
  714.                 }
  715.             out_buffer[out_index++] = (char) b;
  716.         }
  717.         }
  718.         if (saveOutput && (out_index > 0)) {
  719.             Count = (long) out_index;
  720.             if (Err(FSWrite(out_ref_num, &Count, &out_buffer),errWriting)) {
  721.                 *eflag = TRUE;
  722.                 return 0;
  723.             }
  724.         }
  725.     }
  726.     return(crc & 0xffff);
  727. }
  728.  
  729. /*
  730. Convert four input characters into a long.
  731. #################################   Get4   #################################
  732. */
  733. long
  734. get4(bp)
  735. char *bp;
  736. {
  737.     register int i;
  738.     long value = 0;
  739.  
  740.     for (i = 0; i < 4; i++) {
  741.         value <<= 8;
  742.         value |= (*bp & BYTEMASK);
  743.         bp++;
  744.     }
  745.     return(value);
  746. }
  747.  
  748. /*
  749. Read and return a two-byte CRC from the input file.
  750. ################################   GetCrc   ################################
  751. */
  752. crctype getcrc(eflag)
  753. Boolean *eflag;
  754. {
  755.     int value1, value2;
  756.     
  757.     value1 = getbyte(eflag) & BYTEMASK; if (*eflag) return 0;
  758.     value2 = getbyte(eflag) & BYTEMASK; if (*eflag) return 0;
  759.     return((value1 << 8) | value2);
  760. }
  761.  
  762. /*
  763. Copy n characters from p2 to p1.
  764. #################################   Copy   #################################
  765. */
  766. copy(p1, p2, n)
  767. char *p1, *p2;
  768. int n;
  769. {
  770.     while (n-- > 0)
  771.         *p1++ = *p2++;
  772. }
  773.  
  774.  
  775. /*
  776. Return the next bit in the input stream (MSB first).
  777. ################################   GetBit   ################################
  778. */
  779. static char b;
  780. getbit(eflag)
  781. Boolean *eflag;
  782. {
  783.     if (bit == 0) {
  784.         b = read_byte(eflag) & 0xff; if (*eflag) return 0;
  785.         bit = 8;
  786.     }
  787.     bit--;
  788.     return((b >> bit) & 1);
  789. }
  790.  
  791. /*
  792. Get the next virtual byte from the input file.
  793. ###############################   GetByte   ################################
  794.  
  795.     This routine returns the next 8 bits.  If decoding is on, it finds the
  796.     byte in the decoding tree based on the bits from the input stream.  If
  797.     decoding is not on, it either gets it directly from the input stream or
  798.     puts it together from 8 calls to getbit(), depending upon whether or not
  799.     we are currently on a byte boundary.  (Note: the calls to getbit() have
  800.     been expanded inline in order to gain some speed . . . ((b >> bit) & 1)
  801.     is used where the value of getbit(eflag) was used before, and it's
  802.     preceded by the statements "if (bit == 0 {...}" and "bit--").
  803.     
  804. */
  805. getbyte(eflag)
  806. Boolean *eflag;
  807. {
  808.     register nodeptr np;
  809.     register int i, byt;
  810.     
  811.     if (decode) {
  812.         np = nodelist;
  813.         while (np->flag == 0) {
  814.             if (bit == 0) {
  815.                 b = read_byte(eflag) & 0xff;
  816.                 if (*eflag) return 0;
  817.                 bit = 8;
  818.             }
  819.             bit--;
  820.             np = ((int) ((b >> bit) & 1)) ? np->one : np->zero;
  821.         }
  822.         byt = np->byte;
  823.     }
  824.     else {
  825.         if (bit == 0) {    /* on byte boundary? */
  826.             byt = read_byte(eflag) & 0xff; if (*eflag) return 0;
  827.             }
  828.         else {        /* no, put a byte together */
  829.             byt = 0;
  830.             for (i = 8; i > 0; i--) {
  831.                 if (bit == 0) {
  832.                     b = read_byte(eflag) & 0xff;
  833.                     if (*eflag) return 0;
  834.                     bit = 8;
  835.                 }
  836.                 bit--;
  837.                 byt = (byt << 1) + (int) ((b >> bit) & 1);
  838.             }
  839.         }
  840.     }
  841.     return(byt);
  842. }
  843.  
  844. /*
  845. Create a Packit-I or -II format file containing files selected by the user.
  846. #################################   Pack   ################################
  847. */
  848. pack()
  849. {   SFReply PitRecord;
  850.     Point   where;
  851.     int     pit_ref_num, i;
  852.     Boolean Success;
  853.     char   *msg;
  854.  
  855.     /* Get name of output file */
  856.     where.h = PUT_FILE_X; where.v = PUT_FILE_Y;
  857.     msg = Compress ? "\PSave Packit-II file as:" : "\PSave Packit-I file as:";
  858.     SFPutFile(where,msg,"\PPackit.pit",0L,&PitRecord);
  859.     if (! PitRecord.good) return;
  860.  
  861.     /* Create and open output file */
  862.     if ((i=Create(PitRecord.fName,PitRecord.vRefNum,'UPIT','PIT '))==dupFNErr) {
  863.         FSDelete(PitRecord.fName,PitRecord.vRefNum);
  864.         i = Create(PitRecord.fName, PitRecord.vRefNum, 'UPIT', 'PIT ');
  865.     }
  866.     if (Err(i,errCreateFile)) return;
  867.     if (Err(FSOpen(PitRecord.fName,PitRecord.vRefNum,&pit_ref_num),
  868.             errOpenDataFk)) return;
  869.  
  870.      /* Call routine that does real work */
  871.      Success = pack_files(pit_ref_num);
  872.      FSClose(pit_ref_num);
  873.      if (! Success) FSDelete(PitRecord.fName, PitRecord.vRefNum);
  874.      FlushVol(0L, PitRecord.vRefNum);
  875. }
  876.  
  877.  
  878. /*
  879. Pass the indicated file header, data fork, resource fork, and computed CRCs
  880. through the supplied function "MyWrite".  This function saves some space in
  881. "pack_files", as compressed files need to be scanned twice (once to get the
  882. frequency counts, once to write the Huffman encodings for the bytes).
  883.  
  884. ###########################    Run_Through_File   ##########################
  885. */
  886. run_through_file(MyWrite,pit_ref_num,fileHeader,InpRecord,data_len,rsrc_len)
  887. int          (*MyWrite)();
  888. int            pit_ref_num;
  889. unsigned char  *fileHeader;
  890. long        data_len, rsrc_len;
  891. SFReply        *InpRecord;
  892. {   long         Count, bytesLeft;
  893.     register crctype crc;
  894.     unsigned char    io_buffer[BSIZE], fileCRC[2];
  895.     int             inp_ref_num;
  896.  
  897.  
  898.     /* Write file header */
  899.     Count = HDRBYTES + 2;
  900.     if (Err((*MyWrite)(pit_ref_num, &Count, fileHeader), errWriting))
  901.         goto error;
  902.  
  903.     /* Initialize data/resource CRC */
  904.     crc = 0;
  905.  
  906.     /* Copy file's data fork to Packit file, if it has one */
  907.     if (bytesLeft = data_len) {
  908.         if (Err(FSOpen(InpRecord->fName,InpRecord->vRefNum,&inp_ref_num),
  909.                       errOpenDataFk)) goto error;
  910.         while (bytesLeft > 0L) {
  911.             Count = (bytesLeft >= BSIZE) ? BSIZE : bytesLeft;
  912.             if (Err(  FSRead  (inp_ref_num,&Count,io_buffer),errReading) ||
  913.             Err((*MyWrite)(pit_ref_num,&Count,io_buffer),errWriting)) {
  914.             FSClose(inp_ref_num);
  915.                 goto error;
  916.             }
  917.             crc = upd_crc(crc, io_buffer, Count);
  918.             bytesLeft -= Count;
  919.         }
  920.         FSClose(inp_ref_num);
  921.     }
  922.  
  923.     /* Copy file's resource fork to Packit file, if it has one */
  924.     if (bytesLeft = rsrc_len) {
  925.         if (Err(Open_Rsrc(InpRecord->fName,InpRecord->vRefNum,fsRdPerm,
  926.                       &inp_ref_num),errOpenRsrcFk)) goto error;
  927.         while (bytesLeft > 0L) {
  928.             Count = (bytesLeft >= BSIZE) ? BSIZE : bytesLeft;
  929.             if (Err(  FSRead  (inp_ref_num,&Count,io_buffer),errReading) ||
  930.             Err((*MyWrite)(pit_ref_num,&Count,io_buffer),errWriting)) {
  931.             FSClose(inp_ref_num);
  932.                 goto error;
  933.             }
  934.             crc = upd_crc(crc, io_buffer, Count);
  935.             bytesLeft -= Count;
  936.         }
  937.         FSClose(inp_ref_num);
  938.     }
  939.  
  940.     /* Write file's data/resource CRC */
  941.     fileCRC[0] = (((unsigned int) crc) >> 8) & 0xff;
  942.     fileCRC[1] = (((unsigned int) crc) & 0xff);
  943.     Count = 2;
  944.     if (Err((*MyWrite)(pit_ref_num,&Count,fileCRC),errWriting)) goto error;
  945.     return FALSE;
  946. error:
  947.     return TRUE;
  948. }
  949.  
  950.  
  951. /*
  952. Allow FSWrite() to be passed as a parameter to Run_Through_File (above)
  953. ###############################   C_FSWrite   ##############################
  954. */
  955. int C_FSWrite(arg1, arg2, arg3)
  956. int    arg1;
  957. long   *arg2;
  958. char   *arg3;
  959. {
  960.     return FSWrite(arg1, arg2, arg3);
  961. }
  962.  
  963.  
  964. /*
  965. Initialize frequency counts to zero.
  966. ###############################   InitCount   ##############################
  967. */
  968. void init_count()
  969. {   int i;
  970.  
  971.     for (i = 0; i < 256; i++) {
  972.         nodelist[i].flag   = TRUE;
  973.         nodelist[i].byte   = i;
  974.         nodelist[i].index2 = 0;
  975.         nodelist[i].count  = 0L;
  976.         nodelist[i].zero   = 0L;
  977.         nodelist[i].one    = 0L;
  978.         }
  979. }
  980.  
  981.  
  982. /*
  983. Build byte frequency counts on a buffer-by-buffer basis.
  984. ###############################   FreqCount   ##############################
  985. */
  986. int freq_count(Dummy, Count, Buffer)
  987. int            Dummy;
  988. long          *Count;
  989. unsigned char *Buffer;
  990. {   long MyCount = *Count;
  991.  
  992.     while (MyCount--) {
  993.         nodelist[*Buffer].count += 1;
  994.         Buffer++;
  995.     }
  996.     return noErr;
  997. }
  998.  
  999.  
  1000. /* Macros to simplify using an ordering relationship with a secondary key */
  1001. #define GTR(a,b) ((a->count  > b->count) ||\
  1002.                  ((a->count == b->count) && (a->index2  > b->index2)))
  1003. #define LEQ(a,b) ((a->count  < b->count) ||\
  1004.                  ((a->count == b->count) && (a->index2 <= b->index2)))
  1005.  
  1006.  
  1007. /*
  1008. Insert a new value into the heap in O(log(n)) time.
  1009. ##############################   HeapInsert   ##############################
  1010. */
  1011. void heapinsert(n)    /* standard heap insertion algorithm from a textbook */
  1012. int n;            /* inserts heap[n] into heap stored in heap[1...n-1] */
  1013. {   int      i, j;
  1014.     nodeptr  item;
  1015.  
  1016.     j = n;  i = n / 2;  item = heap[n];
  1017.     while ((i > 0) && GTR(heap[i], item)) {
  1018.         heap[j] = heap[i];
  1019.         j = i; i = i / 2;
  1020.     }
  1021.     heap[j] = item;
  1022. }
  1023.  
  1024.  
  1025. /*
  1026. Turn a tree whose left & right sons are heaps into a heap in O(log(n)) time.
  1027. ##############################   HeapAdjust   ##############################
  1028. */
  1029. void heapadjust(i, n)    /* standard heap adjustment algorithm */
  1030. int i, n;
  1031. {   int      j;
  1032.     nodeptr  item;
  1033.  
  1034.     j = 2 * i; item = heap[i];
  1035.     while (j <= n) {
  1036.         if ((j < n) && GTR(heap[j], heap[j+1])) j++;
  1037.         if (LEQ(item, heap[j])) break;
  1038.         heap[j / 2] = heap[j];
  1039.         j = 2 * j;
  1040.     }
  1041.     heap[j / 2] = item;
  1042. }
  1043.  
  1044.  
  1045. /*
  1046. Return the heap element with the smallest value and delete it from the heap.
  1047. ################################   HeapMin   ###############################
  1048. */
  1049. nodeptr heapmin(n)
  1050. int *n;
  1051. {   nodeptr item;
  1052.  
  1053.     item = heap[1];
  1054.     heap[1] = heap[*n];
  1055.     if (*n -= 1) heapadjust(1, *n);
  1056.     return item;
  1057. }
  1058.  
  1059.  
  1060. /*
  1061. Build a minimum weighted binary tree using the counts in nodelist[0..255].
  1062. ###############################   BuildTree   ##############################
  1063. */
  1064. nodeptr build_tree()
  1065. {   int      i, n, gen;
  1066.     nodeptr  lson, rson, nnod, free;
  1067.          
  1068.     n = 0; free = 0L; gen = 0;
  1069.     for (i = 256; i < 512; i++) {    /* All nodes that we know don't */
  1070.         nodelist[i].zero = free;    /*     contain frequency counts */
  1071.         free = &nodelist[i];        /*     go to the free node list */
  1072.         }
  1073.     for (i = 0; i < 256; i++)
  1074.         if (nodelist[i].count) {    /* If a particular byte doesn't */
  1075.             heap[++n] = &nodelist[i];    /*     occur at all, it doesn't */
  1076.             if (n > 1) heapinsert(n);    /*     get placed into the tree */
  1077.         } else {            /*     since it would just make */
  1078.             nodelist[i].zero = free;    /*     the header larger . . .  */
  1079.             free = &nodelist[i];
  1080.         }
  1081.     while (n >= 2) {
  1082.         lson = heapmin(&n);        /* The standard algorithm which */
  1083.         rson = heapmin(&n);        /*     we use to build the tree */
  1084.     nnod = free;            /*     is quite elegant...given */
  1085.     free = free->zero;        /*     W1, W2, .. Wn one simply */
  1086.     nnod->flag   = FALSE;        /*     solves a smaller problem */
  1087.     nnod->zero   = lson;            /*     W1 + W2, W3, .. Wn until */
  1088.     nnod->one    = rson;        /*     one gets the trivial W1. */
  1089.         nnod->count  = lson->count + rson->count;
  1090.         nnod->index2 = ++gen;           /* Use a secondary key to place */
  1091.         heap[++n]    = nnod;        /*     W1 + W2 subtree so as to */
  1092.         if (n > 1) heapinsert(n);    /*     minimize unweighted sum. */
  1093.         }
  1094.     return heapmin(&n);
  1095. }
  1096.  
  1097. /*
  1098. Write a single bit to the output file.
  1099. ################################   PutBit   ###############################
  1100.  
  1101.     Note that this routine reuses a buffer that was allocated for Unpack.
  1102.  
  1103. */
  1104. char OutChar;
  1105. int  BitCount;
  1106.  
  1107. putbit(bit, file)
  1108. int bit, file;
  1109. {   long Count;
  1110.  
  1111.     OutChar <<= 1;
  1112.     if (bit) OutChar |= 1;
  1113.     if (++BitCount == 8) {
  1114.         pit_buffer[pit_index++] = OutChar;
  1115.         BitCount = 0; OutChar = (char) 0;
  1116.         if (pit_index == BSIZE) {
  1117.             Count = BSIZE;
  1118.             pit_index = 0;
  1119.             return FSWrite(file, &Count, pit_buffer);
  1120.         }
  1121.     }
  1122.     return noErr;
  1123. }
  1124.  
  1125.  
  1126. /*
  1127. Write a byte to the output file, using eight calls to putbit().
  1128. ###############################   PutByte   ###############################
  1129. */
  1130. putbyte(byt, file)
  1131. int byt, file;
  1132. {   int retval, i;
  1133.  
  1134.     for (i = 7; i >= 0; i--) {
  1135.         retval = putbit((byt & (1 << i)) ? 1 : 0, file);
  1136.         if (retval != noErr) return retval;
  1137.     }
  1138.     return noErr;
  1139. }
  1140.  
  1141. /*
  1142. Initialize various status variables before outputting a compressed file.
  1143. #############################   Init_Output   #############################
  1144. */
  1145. void init_output()
  1146. {
  1147.     pit_index = 0;
  1148.     BitCount  = 0;
  1149.     OutChar   = (char) 0;
  1150. }
  1151.  
  1152. /*
  1153. Add needed bit padding and empty buffer after outputting a compressed file.
  1154. #############################   Flush_Output   ############################
  1155. */
  1156. int flush_output(file)
  1157. int file;
  1158. {   int  errcode;
  1159.     long Count;
  1160.  
  1161.     while (BitCount) { if (errcode = putbit(0, file)) return errcode; }
  1162.     if  (pit_index)  {
  1163.         Count = pit_index;
  1164.         return FSWrite(file, &Count, pit_buffer);
  1165.     }
  1166.     return noErr;
  1167. }
  1168.  
  1169.  
  1170. /*
  1171. Write a copy of the Huffman tree we're using for compression to pit_file.
  1172. ##############################   Write_Tree   #############################
  1173. */
  1174. int write_tree(root, file)
  1175. nodeptr   root;
  1176. int       file;
  1177. {   int   errno;
  1178.  
  1179.     if (root->flag) {
  1180.         if (errno = putbit    (1,          file))  return errno;
  1181.         if (errno = putbyte   (root->byte, file))  return errno;
  1182.     } else {
  1183.         if (errno = putbit    (0,          file))  return errno;
  1184.         if (errno = write_tree(root->zero, file))  return errno;
  1185.         if (errno = write_tree(root->one , file))  return errno;
  1186.     }
  1187.     return noErr;
  1188. }
  1189.  
  1190. /*
  1191. Walk the minimum-total-cost tree to associate bit sequences with each byte.
  1192. #############################   Build_Sequences   #########################
  1193. */
  1194. Boolean build_sequences(nd, bits, bitcount)
  1195. nodeptr nd;
  1196. long    bits;
  1197. int     bitcount;
  1198. {
  1199.     if (nd->flag) {
  1200.         byte_code[nd->byte].bcount = bitcount;
  1201.         byte_code[nd->byte].bits   = bits;
  1202.     } else {
  1203.         if ((bitcount == 32) || (! build_sequences(nd->zero, bits, bitcount+1))
  1204.          ||   (! build_sequences(nd->one , bits | (1 << bitcount), bitcount+1)))
  1205.             return FALSE;
  1206.     }
  1207.     return TRUE;
  1208. }
  1209.  
  1210.  
  1211. /*
  1212. Write the contents of a buffer using precomputed Huffman compression strings.
  1213. ###############################   PackWrite   ##############################
  1214. */
  1215. int pack_write(pit_ref_num, Count, Buffer)
  1216. int            pit_ref_num;
  1217. long          *Count;
  1218. unsigned char *Buffer;
  1219. {   long     theBits, MyCount = *Count;
  1220.     int      i, e;
  1221.  
  1222.     while (MyCount--) {
  1223.         theBits = byte_code[*Buffer].bits;
  1224.         for (i  = byte_code[*Buffer++].bcount; i; i--) {
  1225.             if (e = putbit((int) (theBits & 1), pit_ref_num)) return e;
  1226.             theBits = theBits >> 1;
  1227.         }
  1228.     }
  1229.     return noErr;
  1230. }
  1231.  
  1232.  
  1233. /*
  1234. Copy files into the file denoted by pit_ref_num until error or user done.
  1235. ##############################   Pack_Files   #############################
  1236. */
  1237. Boolean pack_files(pit_ref_num)
  1238. int pit_ref_num;
  1239. {   SFReply          InpRecord;
  1240.     ParamBlockRec    ParamB;
  1241.     Point            where;
  1242.     unsigned char    fileHeader[HDRBYTES+2];
  1243.     long             Count;
  1244.     int             i, errno;
  1245.     register crctype crc;
  1246.     DialogPtr        theDialog = 0L;
  1247.     nodeptr         root;
  1248.     
  1249.     /* Loop as long as the user is willing to supply files */
  1250.     while (TRUE) {
  1251.         where.h = GET_FILE_X; where.v = GET_FILE_Y;
  1252.         SFGetFile(where, "", 0L, -1, 0L, 0L, &InpRecord);
  1253.         if (! InpRecord.good) {
  1254.             Count = 4;
  1255.             return (! Err(FSWrite(pit_ref_num, &Count, "PEnd"), errWriting));
  1256.         }
  1257.         
  1258.     ParamText(0L, 0L, "\PPack", InpRecord.fName);
  1259.         theDialog = GetNewDialog(statusid, 0L, -1L);
  1260.         DrawDialog(theDialog);
  1261.         
  1262.     ParamB.fileParam.ioCompletion = 0L;
  1263.     ParamB.fileParam.ioNamePtr = InpRecord.fName;
  1264.     ParamB.fileParam.ioVRefNum = InpRecord.vRefNum;
  1265.     ParamB.fileParam.ioFVersNum = 0;
  1266.     ParamB.fileParam.ioFDirIndex = 0;
  1267.     if (Err(PBGetFInfo(&ParamB,0),errGetFInfo)) goto error;
  1268.  
  1269.     for (i = 0; i < 64; i++) fileHeader[i] = InpRecord.fName[i];
  1270.     put4(&fileHeader[H_TYPEOFF], ParamB.fileParam.ioFlFndrInfo.fdType);
  1271.     put4(&fileHeader[H_AUTHOFF], ParamB.fileParam.ioFlFndrInfo.fdCreator);
  1272.     fileHeader[H_FLAGOFF  ] = (ParamB.fileParam.ioFlFndrInfo.fdFlags >> 8)
  1273.                    & 0xff;
  1274.     fileHeader[H_FLAGOFF+1] = (ParamB.fileParam.ioFlFndrInfo.fdFlags
  1275.                    & 0xff);
  1276.     fileHeader[H_LOCKOFF  ] = 0;
  1277.     fileHeader[H_LOCKOFF+1] = (ParamB.fileParam.ioFlAttrib & FLOCK) ? 1 : 0;
  1278.     put4(&fileHeader[H_DLENOFF], ParamB.fileParam.ioFlLgLen);
  1279.     put4(&fileHeader[H_RLENOFF], ParamB.fileParam.ioFlRLgLen);
  1280.     put4(&fileHeader[H_CTIMOFF], ParamB.fileParam.ioFlCrDat);
  1281.     put4(&fileHeader[H_MTIMOFF], ParamB.fileParam.ioFlMdDat);
  1282.  
  1283.     crc = upd_crc(0, fileHeader, (long) HDRBYTES);
  1284.     fileHeader[HDRBYTES  ] = (crc >> 8) & 0xff;
  1285.     fileHeader[HDRBYTES+1] = (crc & 0xff);
  1286.     
  1287.     if (Compress) {
  1288.             Count = 4L;
  1289.         if (Err(FSWrite(pit_ref_num,&Count,"PMa4"), errWriting)) goto error;
  1290.         init_count();  init_output();
  1291.         if (run_through_file(freq_count, pit_ref_num, fileHeader,
  1292.                      &InpRecord, ParamB.fileParam.ioFlLgLen,
  1293.                      ParamB.fileParam.ioFlRLgLen)) goto error;
  1294.         root = build_tree();
  1295.         if (Err(write_tree(root, pit_ref_num), errWriting)) goto error;
  1296.         if (! build_sequences(root,0L,0)) {Err2(errCompress); goto error;}
  1297.         if (run_through_file(pack_write, pit_ref_num, fileHeader,
  1298.                      &InpRecord, ParamB.fileParam.ioFlLgLen,
  1299.                      ParamB.fileParam.ioFlRLgLen)) goto error;
  1300.         if (Err(flush_output(pit_ref_num), errWriting)) goto error;
  1301.     } else {
  1302.             Count = 4L;
  1303.         if (Err(FSWrite(pit_ref_num, &Count, "PMag"), errWriting) ||
  1304.             run_through_file(C_FSWrite, pit_ref_num, fileHeader,
  1305.                              &InpRecord, ParamB.fileParam.ioFlLgLen,
  1306.                              ParamB.fileParam.ioFlRLgLen)) goto error;
  1307.     }
  1308.         
  1309.     /* Remove status dialog from screen */
  1310.     DisposDialog(theDialog); theDialog = 0L;
  1311.     }
  1312. error:                /* should not be reached except by "goto" */
  1313.     if (theDialog != 0L)
  1314.         DisposDialog(theDialog);
  1315.     return FALSE;
  1316. }
  1317.  
  1318. /*
  1319. Turn a long value into four characters.
  1320. #################################   Put4   ################################
  1321. */
  1322. put4(str, data)
  1323. unsigned char *str;
  1324. long  data;
  1325. {   int i;
  1326.  
  1327.     for (i = 3; i >= 0; i--) {
  1328.         str[i] = data & BYTEMASK;
  1329.         data >>= 8;
  1330.     }
  1331. }
  1332.  
  1333.  
  1334. /***************************************************************************/
  1335. /*       Scaffolding to support the Mac user interface, DAs, etc.          */
  1336. /***************************************************************************/
  1337.  
  1338. #define charcodemask 255
  1339.  
  1340. #define lastmenu 3    /* number of menus */
  1341. #define applemenu 1    /* menu ID for desk accessory menu */
  1342. #define filemenu 2    /* menu ID for File menu */
  1343. #define editmenu 3    /* menu ID for Edit menu */
  1344.  
  1345. #define iunpack 1    /* items in the File menu */
  1346. #define ifilter 2
  1347.  /*-------*/
  1348. #define idelete 4
  1349.  /*-------*/
  1350. #define ipack   6
  1351. #define icompress 7
  1352.  /*-------*/
  1353. #define ihelp   9
  1354.  /*-------*/
  1355. #define iquit  11
  1356.  
  1357. MenuHandle    mymenus[lastmenu+1];
  1358. EventRecord    myevent;
  1359. WindowPtr    whichwindow;
  1360. int        windowcode;
  1361. Boolean        userdone;
  1362. #define maxStackSize 8192
  1363.  
  1364. setup()
  1365. {   int i;
  1366.  
  1367.     /* Do various magical things to set up memory (this is all from SKEL) */
  1368.     typedef long *lomemptr;    /* a pointer to low memory locations */
  1369.     lomemptr nilptr;        /* will have value NIL */
  1370.     lomemptr stackbaseptr;    /* points to current stack base */
  1371.     nilptr = 0L; *nilptr = (long) -1;    /* for ID=02 bomb on dereferencing 0 */
  1372.     stackbaseptr = (lomemptr) 0x908;    /* CurStackBase */
  1373.     SetApplLimit ((Ptr) (*stackbaseptr - maxStackSize));
  1374.     MaxApplZone();
  1375.     MoreMasters(); MoreMasters(); MoreMasters();
  1376.     
  1377.     InitGraf(&thePort);
  1378.     InitFonts();
  1379.     FlushEvents(everyEvent, 0);
  1380.     InitWindows();
  1381.     InitMenus();
  1382.     TEInit();
  1383.     InitDialogs(0L);
  1384.     InitCursor();
  1385.     SetEventMask(everyEvent - keyUpMask);
  1386.     
  1387.  
  1388.     /* I can think of no reason why calling GetMenu() in a loop should */
  1389.     /* have different results than unrolling the loop, but if MENU_BUG */
  1390.     /* is defined, Unpit will bomb (or drop into Macsbug) when run.  I */
  1391.     /* suspect that the bombs are a result of a bug in LightSpeed C... */
  1392.     
  1393.     /* Update: October 1, 1986.  As suggested by Steve Stein of THINK, */
  1394.     /* the problem is caused by SetEventMask in the LSC 1.0.2 MacTraps */
  1395.     /* library.  The fix is to search for hexadecimal 225f21df01444ed1 */
  1396.     /* using FEdit/equivalent and change the third byte from 21 to 31. */
  1397.  
  1398. #ifdef MENU_BUG
  1399.     for (i = applemenu; i <= editmenu; i++)
  1400.         mymenus[i] = GetMenu(i);
  1401. #else
  1402.     mymenus[applemenu] = GetMenu(applemenu);
  1403.     mymenus[filemenu]  = GetMenu(filemenu);
  1404.     mymenus[editmenu]  = GetMenu(editmenu);
  1405. #endif
  1406.  
  1407.     AddResMenu(mymenus[applemenu],'DRVR');
  1408.     Filter = TRUE;
  1409.     CheckItem(mymenus[filemenu],ifilter,/* Filter ? */ 255 /* :0 */);
  1410.     Compress = TRUE;
  1411.     CheckItem(mymenus[filemenu],icompress,/* Compress ? */ 255 /* :0 */);
  1412.  
  1413.     for (i = applemenu; i <= editmenu; i++)
  1414.         InsertMenu(mymenus[i],0);
  1415.     DrawMenuBar();
  1416. }
  1417.  
  1418. /*
  1419. Handle a command given through a menu selection
  1420. ############################   DoCommand   ##############################
  1421.  
  1422.    We carry out the command indicated by mResult.
  1423.    If it was Quit, we return true, else false.  Since the menu was
  1424.    highlighted by MenuSelect, we must finish by unhighlighting it
  1425.    to indicate we're done.
  1426. */
  1427. Boolean docommand (mresult)
  1428. long    mresult;
  1429. {
  1430.     int     refnum;
  1431.     int     themenu,
  1432.             theitem;
  1433.     char    name[255];
  1434.     GrafPtr saveport;        /* for saving current port in when opening
  1435.                    a desk accessory */
  1436.     Boolean returns;
  1437.     
  1438.     
  1439. #define aboutboxid 257
  1440. #define helponeid  261
  1441. #define helptwoid  262
  1442.     DialogPtr theDialog;
  1443.     int itemhit;
  1444.  
  1445.     returns = FALSE;        /* assume Quit not selected */
  1446.     themenu = HiWord (mresult);    /* get the menu selected */
  1447.     theitem = LoWord (mresult);    /* ... and the Item of that menu */
  1448.     switch (themenu) {
  1449.     case 0: 
  1450.         break;        /* user made no selection; do nothing */
  1451.  
  1452.     case applemenu: 
  1453.         if (theitem == 1) { /* tell about the program */
  1454.         theDialog = GetNewDialog(aboutboxid, 0L, -1L);
  1455.         ModalDialog(0L, &itemhit);
  1456.         DisposDialog(theDialog);
  1457.         }
  1458.         else {        /* run a desk accessory; make sure port is preserved */
  1459.         GetPort (&saveport);
  1460.         GetItem (mymenus[applemenu], theitem, name);  /* get name */
  1461.         refnum = OpenDeskAcc (name);/* run the desk accessory */
  1462.         SetPort (saveport);
  1463.             }
  1464.         break;
  1465.  
  1466.     case filemenu: 
  1467.         switch (theitem) {
  1468.         case iunpack:
  1469.             unpack();
  1470.             break;
  1471.         case ifilter:
  1472.             Filter = (Filter ? FALSE : TRUE);
  1473.             CheckItem(mymenus[filemenu],ifilter,(Filter) ? 255 : 0);
  1474.             break;
  1475.         case idelete:
  1476.             delete();
  1477.             break;
  1478.         case ipack:
  1479.             pack();
  1480.             break;
  1481.         case icompress:
  1482.             Compress = (Compress ? FALSE : TRUE);
  1483.             CheckItem(mymenus[filemenu],icompress,(Compress) ? 255 : 0);
  1484.             break;
  1485.         case ihelp:    /* tell how to use the program */
  1486.             theDialog = GetNewDialog(helponeid, 0L, -1L);
  1487.             ModalDialog(0L, &itemhit);
  1488.             DisposDialog(theDialog);
  1489.             theDialog = GetNewDialog(helptwoid, 0L, -1L);
  1490.             ModalDialog(0L, &itemhit);
  1491.             DisposDialog(theDialog);
  1492.             break;
  1493.         case iquit:
  1494.             returns = TRUE;
  1495.             break;
  1496.         }            /* fileMenu case */
  1497.         break;
  1498.         
  1499.     case editmenu:
  1500.         SystemEdit(theitem-1);
  1501.         break;
  1502.     }                /* menu case */
  1503.  
  1504.     HiliteMenu (0);        /* turn off hilighting on the menu just used */
  1505.     return (returns);
  1506. }                /* DoCommand */
  1507.  
  1508.  
  1509.  
  1510. maineventloop () {
  1511.  
  1512. /* body of MainEventLoop */
  1513.  
  1514.     FlushEvents (everyEvent, 0);    /* discard leftover events */
  1515.  
  1516. /* get next event, and Handle it appropriately, until user QUITs */
  1517.  
  1518.     userdone = FALSE;
  1519.     do {
  1520.     SystemTask ();        /* Handle desk accessories */
  1521.     if (GetNextEvent (everyEvent, &myevent)) {
  1522.                 /* get event; if for us... */
  1523.         switch (myevent.what) {/* Handle each kind of event */
  1524.         case mouseDown: /* find out what window the mouse went
  1525.                    down in, and where in it */
  1526.             windowcode = FindWindow (myevent.where, &whichwindow);
  1527.             switch (windowcode) { /* Handle mouse-down for each place */
  1528.             case inSysWindow:
  1529.                 /* Handle the desk accessories */
  1530.                 SystemClick (&myevent, whichwindow);
  1531.                 break;/* inSysWindow */
  1532.             case inMenuBar: /* Handle the command */
  1533.                 userdone = docommand (MenuSelect (myevent.where));
  1534.                 break;/* inMenuBar */
  1535.             }
  1536.             break;    /* switch */
  1537.  
  1538.         case keyDown: 
  1539.         case autoKey:     /* if command key, pass the char to MenuKey */
  1540.             if ((myevent.modifiers & cmdKey) != 0)
  1541.             userdone = docommand (MenuKey ((char) (myevent.message
  1542.                                   & charcodemask)));
  1543.             break;      /* switch */
  1544.         case diskEvt:
  1545.             if (HiWord(myevent.message) != noErr) {
  1546.                 Point where;
  1547.                 where.h = INIT_X; where.v = INIT_Y;
  1548.                 DIBadMount(where, myevent.message);
  1549.             }
  1550.             break;    /* switch */
  1551.             
  1552.         }
  1553.     }
  1554.  
  1555.     } while (userdone == FALSE);
  1556. }
  1557.  
  1558. main()
  1559. {   Handle Args;
  1560.     char OurName[64];
  1561.     int OurRefNum, ArgCount, ArgType;
  1562.     AppFile *ArgFile;
  1563.     ParamBlockRec ParamB;
  1564.     Boolean even;
  1565.     
  1566.     setup();
  1567.     DILoad();
  1568.     GetAppParms(OurName, &OurRefNum, &Args);
  1569.     ArgType  = **((int **) Args);
  1570.     ArgCount = *(1 + ((int *) (*Args)));
  1571.     if (ArgCount == 0)
  1572.         maineventloop();
  1573.     else
  1574.         if (ArgType != appOpen)
  1575.             Err2(errBadPrint);
  1576.         else {
  1577.             HLock(Args);
  1578.             ArgFile = (AppFile *) (4 + *Args);
  1579.             while (ArgCount-- > 0) {
  1580.                 ParamB.fileParam.ioCompletion = 0L;
  1581.                 ParamB.fileParam.ioNamePtr = ArgFile->fName;
  1582.                 ParamB.fileParam.ioVRefNum = ArgFile->vRefNum;
  1583.                 ParamB.fileParam.ioFVersNum = 0;
  1584.                 ParamB.fileParam.ioFDirIndex = 0;    /* NamePtr/VRefNum */
  1585.                 PBGetFInfo(&ParamB,0);
  1586.                 pit_bytes_left = ParamB.fileParam.ioFlLgLen;
  1587.                 if (pit_bytes_left < 4L)
  1588.                     /* this is more meaningful than "I/O error", */
  1589.                     /* but we should also print the filename ... */
  1590.                     Err2(errNotPackit);
  1591.                 else {
  1592.                     pit_index = 0; pit_max_index = -1;
  1593.                     FSOpen(ArgFile->fName,ArgFile->vRefNum,&pit_ref_num);
  1594.                     unpit();
  1595.                     FSClose(pit_ref_num);
  1596.                 }
  1597.                 even = ((ArgFile->fName[0] >> 1) * 2) == ArgFile->fName[0];
  1598.                 ArgFile = (AppFile *) (9 + ArgFile->fName[0] + (even ? 1 : 0)
  1599.                                + (char *) ArgFile);
  1600.             }
  1601.             HUnlock(Args);
  1602.         }
  1603.     DIUnload();
  1604. }
  1605.  
  1606.