home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 28 / amigaformatcd28.iso / -seriously_amiga- / archivers / mcvertppc / hqxify.c < prev    next >
C/C++ Source or Header  |  1998-04-27  |  29KB  |  1,027 lines

  1. /* see "changes.powerup" for PPC/Amiga port related changes */
  2.  
  3. #include "mactypes.h"
  4.  
  5. /* HQXBUFLEN must be large enough to hold a complete hqx header */
  6. #define HQXBUFLEN 512
  7. byte hqxbuf[HQXBUFLEN + 1], *buf_ptr, *buf_end, *buf_start = hqxbuf + 1;
  8.  
  9. /* Note: line[0] stores the run length character,
  10.  * so line is one greater than MAXLINE  */
  11. #define MAXLINE 2048
  12. byte line[MAXLINE + 1], *line_ptr, *line_end, *line_start = line + 1;
  13.  
  14. /* keep the compiler happy */
  15. #define LINE_START ((char*)(line_start))
  16.  
  17. extern char *cmdname;
  18.  
  19. int line_count, file_count;
  20. int save_state, total_bytes, save_run_length;
  21. word save_accum;
  22. char binfname[SYSNAMELEN], hqxfname[SYSNAMELEN];
  23. FILE *hqxfile, *binfile;
  24.  
  25. /*
  26.  * hqxsuspect caches whether or not we have gotten past all suspect data
  27.  * at the head of a file, for example, a mail header.  hqxsuspect is set
  28.  * on every new hqx file read and on detecting bogus lines,
  29.  * and reset only in non_suspect().
  30.  */
  31. int hqxsuspect;
  32. extern suspect_shorter;
  33. extern suspect_same;
  34.  
  35. extern int info_only;
  36.  
  37. /* This routine reads the header of a hqxed file and appropriately twiddles it,
  38.     determines if it has CRC problems, creates the .bin file, and puts the info
  39.     into the .bin file.
  40.     Output is hqx_datalen, hqx_rsrclen, type, binfname, binfile.
  41.      Returns 0 iff failed to read header.
  42.  */
  43.  
  44.  
  45. int
  46. hqx_to_bin_hdr(type, hqx_datalen, hqx_rsrclen, same_file_only)
  47.     char *type;
  48.     ulong *hqx_datalen, *hqx_rsrclen;
  49.     int same_file_only;
  50. {
  51.     register byte *hqx_ptr, *hqx_end;
  52.     register ulong calc_crc;
  53.     hqx_buf *hqx_block;
  54.     hqx_header *hqx;
  55.     info_header info;
  56.     ulong mtim;
  57.     short crc;
  58.     long len;
  59.  
  60.     extern word magic[];
  61.     extern char *dir, *ext;
  62.     extern short calc_mb_crc();
  63.  
  64.     /* read the hqx header, 
  65.      * assuming that I won't exhaust hqxbuf in so doing --
  66.      * that is, that hqxbuf is always large enough (it is)
  67.      */
  68.     (void)fill_hqxbuf(same_file_only);
  69.     /*
  70.      * If we are reading multiple files, then we could have the last
  71.      * unread line of the "previous" file be just a colon (since we are
  72.      * length driven, and stopped when we processed the expected
  73.      * lengths), and the prior fill_hqxbuf() call would find it and
  74.      * return 0, leaving the buffer unfilled.  So, if we have zero
  75.      * bytes so far, just fill it again.
  76.      */
  77.     if (total_bytes == 0) {
  78.         DEBUG && fprintf(debug,
  79.             "%s: Note: had to call fill_hqxbuf again in hqx_to_bin_hdr\n",
  80.             cmdname);
  81.         DEBUG && fflush(debug);
  82.         (void)fill_hqxbuf(same_file_only);
  83.     }
  84.     if (same_file_only == 1 && total_bytes == 0)
  85.         return 0;
  86.     if (total_bytes < MIN_HQX_HDR) {
  87.         fprintf(verbose, "%s: %s (%d < %d): bad file format? -- exiting\n",
  88.             cmdname, "error: hqx_header too short", total_bytes, MIN_HQX_HDR);
  89.         fflush(verbose);
  90.         exit(2);
  91.     }
  92.  
  93.     hqx_block = (hqx_buf *) buf_ptr;
  94.     hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen);
  95.     hqx_ptr = buf_ptr;
  96.     hqx_end = (byte *) hqx + sizeof(hqx_header) - 1;
  97.     calc_crc = 0;
  98.     while (hqx_ptr < hqx_end)
  99.         calc_crc = (((calc_crc & 0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8];
  100.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  101.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  102.     buf_ptr = hqx_ptr;
  103.  
  104.     /* stuff the hqx header data into the info header */
  105.     bzero((char*)&info, sizeof(info_header));
  106.     info.nlen = hqx_block->nlen;
  107.     if (info.nlen > sizeof(info.name))
  108.         error("Error: corrupt BinHex data format", "");
  109.     strncpy((char*)info.name, (char*)hqx_block->name, (int)info.nlen);/* name */
  110.  
  111.     /* now make sure the resulting name is valid for length only */
  112.     info.nlen = macify((char*)info.name, (int)info.nlen, 0);
  113.  
  114.     bcopy((char*)hqx->type, (char*)info.type, 9);       /* type, author, flag */
  115.     info.flags &= 0x7e;               /* reset lock bit, init bit */
  116.     if (hqx->protect & 0x40)
  117.         info.protect = 1;           /* copy protect bit */
  118.     bcopy((char*)hqx->dlen, (char*)info.dlen, 8);           /* dlen, rlen */
  119.     mtim = unix2mac((ulong)time((long*)0));
  120.     bcopy((char*)&mtim, (char*)info.mtim, 4);
  121.     bcopy((char*)&mtim, (char*)info.ctim, 4);
  122.     info.uploadvers = '\201';
  123.     info.readvers = '\201';
  124.  
  125.     /* calculate MacBinary CRC */
  126.     crc = calc_mb_crc((unsigned char*)&info, 124L, 0);
  127.     info.crc[0] = (char) (crc >> 8);
  128.     info.crc[1] = (char) crc;
  129.  
  130.     /* Create the .bin file and write the info to it */
  131.  
  132.     unixify((char*)hqx_block->name);
  133.     converting(info.name, (int)info.nlen, info.type, info.auth);
  134.     print_bin_hdr("Creating", &info);
  135.  
  136.     len = strlen(dir) + strlen((char*)hqx_block->name) + strlen(ext) + 1;
  137.     if (len >= sizeof(binfname))
  138.         error("Error: generated binfname would be too long", "");
  139.  
  140. #if defined(__SASC) && defined(AMIGA)
  141.         if(dir[0])
  142.          {
  143.           strcpy(binfname, dir);
  144.           if(dir[strlen(dir)-1] != ':') strcat(dir, "/");
  145.          }else binfname[0] = '\0';
  146.  
  147.         strcat(binfname, hqx_block->name);
  148.         strcat(binfname, ext);
  149. #else
  150.     sprintf(binfname, "%s/%s%s", dir, hqx_block->name, ext);
  151. #endif
  152.  
  153.     binfile = mopen(binfname, "", "w");
  154.  
  155.     check_hqx_crc((word)calc_crc, "File header CRC mismatch in %s", binfname);
  156.     if (1 != fwrite((char*)&info, sizeof(info), 1, binfile))
  157.         error("fwrite failed on binfile", "");
  158.  
  159.     /* Get a couple of items we'll need later */
  160.     bcopy((char*)info.dlen, (char*)hqx_datalen, 4);
  161.     *hqx_datalen = mac2long(*hqx_datalen);
  162.     bcopy((char*)info.rlen, (char*)hqx_rsrclen, 4);
  163.     *hqx_rsrclen = mac2long(*hqx_rsrclen);
  164.     bcopy((char*)info.type, (char*)type, 4);
  165.  
  166.     /* emit useful debugging info */
  167.     DEBUG && fprintf(debug, "\tdata_len=%10ld\t\trsrc_len=%10ld\n",
  168.         *hqx_datalen, *hqx_rsrclen);
  169.     DEBUG && fflush(debug);
  170.  
  171.     return 1;
  172. }
  173.  
  174. /* This routine reads the header of a bin file and appropriately twiddles it,
  175.     creates the .hqx file, and puts the info into the .hqx file.
  176.     Output is hqx_datalen, hqx_rsrclen, type, hqxfname, hqxfile */
  177.  
  178. bin_to_hqx_hdr(hqx_datalen, hqx_rsrclen)
  179.     ulong *hqx_datalen, *hqx_rsrclen;
  180. {
  181.     register byte *hqx_ptr, *hqx_end;
  182.     register ulong calc_crc;
  183.     hqx_buf *hqx_block;
  184.     hqx_header *hqx;
  185.     info_header info;
  186.     extern word magic[];
  187.     extern char **hqxnames_left;
  188.     extern char *ext;
  189.     long len;
  190.  
  191.     len = strlen(*hqxnames_left);
  192.     if (len >= sizeof(binfname))
  193.         error("Error: specified binfname is too long", "");
  194.     strcpy(binfname, *hqxnames_left++);
  195.     binfile = mopen(binfname, ext, "r");
  196.  
  197.     if (!fread((char*)&info, sizeof(info), 1, binfile))
  198.         error("Unexpected EOF in MacBinary header of %s", binfname);
  199.  
  200.     /* stuff the info header into the hqx header */
  201.     hqx_block = (hqx_buf *) buf_ptr;
  202.     hqx_block->nlen = info.nlen;
  203.     if (info.nlen > sizeof(hqx_block->name))
  204.         error("Error: corrupt MacBinary data format", "");
  205.     strncpy((char*)hqx_block->name, (char*)info.name, (int)info.nlen);
  206.     /*
  207.      * We expect a valid Macintosh file name since it came from a MacBinary file
  208.      * so we don't potentially corrupt a valid copied name via macify translate.
  209.      * File name length should not be a problem, so we do nothing.
  210.      */
  211.     hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen);
  212.     hqx->version = 0;
  213.     bcopy((char*)info.type, (char*)hqx->type, 9);   /* type, author, flags */
  214.     hqx->flags &= 0x7e;               /* reset lock bit, init bit */
  215.     if (info.protect == 1)
  216.         hqx->protect = 0;           /* protect bit: 0x40 */
  217.     else
  218.         hqx->protect = 0;
  219.     bcopy((char*)info.dlen, (char*)hqx->dlen, 8);           /* dlen, rlen */
  220.  
  221.     /* Create the .hqx file and write the info to it */
  222.  
  223. #ifdef notdef
  224.        This is the right thing to check but the Sun optimizing
  225.        compiler gives a (valid) warning that info.nlen (one char,
  226.        255 max) is always less than sizeofhqxfname) which is
  227.        hardwired to 1024 since version 1.99.  Since we have already
  228.        checked info.nlen above, we skip the test and avoid the warning.
  229.  
  230.     if (info.nlen >= sizeof(hqxfname))
  231.         error("Error: generated hqxfname would be too long", "");
  232. #endif
  233.  
  234.     strncpy(hqxfname, (char*)info.name, (int)info.nlen);
  235.     hqxfname[info.nlen] = '\0';
  236.     unixify(hqxfname);
  237.     converting(info.name, (int)info.nlen, info.type, info.auth);
  238.     print_bin_hdr("Reading", &info);
  239.  
  240.     calc_crc = 0;
  241.     hqx_ptr = (byte *) hqx_block;
  242.     hqx_end = hqx_ptr + hqx_block->nlen + sizeof(hqx_header);
  243.     while (hqx_ptr < hqx_end)
  244.         calc_crc = (((calc_crc & 0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8];
  245.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  246.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  247.     buf_ptr = hqx_end;
  248.     write_hqx_crc((word)calc_crc);
  249.  
  250.     /* Get a couple of items we'll need later */
  251.     bcopy((char*)info.dlen, (char*)hqx_datalen, 4);
  252.     *hqx_datalen = mac2long(*hqx_datalen);
  253.     bcopy((char*)info.rlen, (char*)hqx_rsrclen, 4);
  254.     *hqx_rsrclen = mac2long(*hqx_rsrclen);
  255. }
  256.  
  257.  
  258. /* This routine copies bytes from the decoded input stream to the output.  
  259.     It also pads to a multiple of 128 bytes on the output, which is part
  260.     of the .bin format */
  261. word
  262. hqx_to_bin_fork(nbytes)
  263.     register ulong nbytes;
  264. {
  265.     register byte *cp;
  266.     register ulong calc_crc;
  267.     register int c_length;
  268.     ulong extra_bytes;
  269.     extern word magic[];
  270.     long avail = 0;    /* used for internal consistency checking */
  271.     int wrote;
  272.  
  273.     extra_bytes = 127 - (nbytes + 127) % 128;    /* pad fork to mult of
  274.                              * 128 bytes */
  275.     calc_crc = 0;
  276.     for (;;) {
  277.         cp = buf_ptr;
  278.         c_length = (cp + nbytes > buf_end) ? buf_end - cp : nbytes;
  279.         /* we can only check readily if we read it here */
  280.         if (avail && c_length > avail)
  281.             error("hqx_to_bin_fork: writing %ld too many bytes",
  282.                 (char*)c_length - avail);
  283.         nbytes -= c_length;
  284.         wrote = fwrite((char*)cp, sizeof(byte), c_length, binfile);
  285.         if (wrote != c_length)
  286.             error("hqx_to_bin_fork: fwrite on binfile wrote %ld bytes too few",
  287.                 (char*)c_length-wrote);
  288.         while (c_length--)
  289.             calc_crc = (((calc_crc & 0xff) << 8) | *cp++) ^ magic[calc_crc >> 8];
  290.         if (!nbytes)
  291.             break;
  292.         avail = fill_hqxbuf(0);
  293.     }
  294.     buf_ptr = cp;
  295.     while (extra_bytes--)
  296.         if (EOF == putc(0, binfile))
  297.             error("Error: putc failed on binfile", "");
  298.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  299.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  300.     return (word) calc_crc;
  301. }
  302.  
  303. /* This routine copies bytes from the input stream to the encoded output.  
  304.     It also pads to a multiple of 128 bytes on the input, which is part
  305.     of the .bin format */
  306. word
  307. bin_to_hqx_fork(nbytes)
  308.     register ulong nbytes;
  309. {
  310.     register byte *cp;
  311.     register ulong calc_crc;
  312.     register int c_length;
  313.     ulong extra_bytes;
  314.     extern word magic[];
  315.  
  316.     extra_bytes = 127 - (nbytes + 127) % 128;    /* pad fork to mult of
  317.                              * 128 bytes */
  318.     calc_crc = 0;
  319.     for (;;) {
  320.         cp = buf_ptr;
  321.         c_length = (cp + nbytes > buf_end) ? buf_end - cp : nbytes;
  322.         nbytes -= c_length;
  323.         if (c_length != fread((char*)cp, sizeof(byte), c_length, binfile))
  324.             error("fread failed on binfile", "");
  325.         buf_ptr += c_length;
  326.         while (c_length--)
  327.             calc_crc = (((calc_crc & 0xff) << 8) | *cp++) ^ magic[calc_crc >> 8];
  328.         if (!nbytes)
  329.             break;
  330.         empty_hqxbuf();
  331.     }
  332.     buf_ptr = cp;
  333.  
  334.     fseek(binfile, (long)extra_bytes, 1);
  335.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  336.     calc_crc = ((calc_crc & 0xff) << 8) ^ magic[calc_crc >> 8];
  337.     return (word) calc_crc;
  338. }
  339.  
  340. /* Essentials for Binhex 8to6 run length encoding */
  341. #define RUNCHAR 0x90
  342. #define MAXRUN 255
  343. #define IS_LEGAL <0x40
  344. #define ISNT_LEGAL >0x3f
  345. #define DONE 0x7F        /* tr68[':'] = DONE, since Binhex terminator is ':' */
  346. #define SKIP 0x7E        /* tr68['\n'|'\r'] = SKIP, i. e. end of line char.  */
  347.     /* We also treat '\0' as SKIP to handle files without trailing newline */
  348.  
  349.     /*
  350.        NOTE: we really don't do a very good job of handling lines
  351.        with ^M's as the end of line character, since we jettison
  352.        the entire UNIX line (which can look like multiple Mac
  353.        lines) when rejecting the line.  Easiest way to fix this is
  354.        to write our own version of fgets which breaks on ^M or ^J.
  355.      */
  356.  
  357. #define FAIL 0x7D        /* character illegal in binhex file */
  358.  
  359. byte tr86[] =
  360. "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
  361. byte tr68[] = {
  362. /* 0x00 */
  363.     SKIP, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  364.     FAIL, FAIL, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL,
  365. /* 0x10 */
  366.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  367.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  368. /* 0x20 */
  369.     FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
  370.     0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, FAIL, FAIL,
  371. /* 0x30 */
  372.     0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, FAIL,
  373.     0x14, 0x15, DONE, FAIL, FAIL, FAIL, FAIL, FAIL,
  374. /* 0x40 */
  375.     0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
  376.     0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, FAIL,
  377. /* 0x50 */
  378.     0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, FAIL,
  379.     0x2C, 0x2D, 0x2E, 0x2F, FAIL, FAIL, FAIL, FAIL,
  380. /* 0x60 */
  381.     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, FAIL,
  382.     0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, FAIL, FAIL,
  383. /* 0x70 */
  384.     0x3D, 0x3E, 0x3F, FAIL, FAIL, FAIL, FAIL, FAIL,
  385.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  386. /* 0x80 */
  387.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  388.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  389. /* 0x90 */
  390.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  391.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  392. /* 0xA0 */
  393.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  394.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  395. /* 0xB0 */
  396.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  397.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  398. /* 0xC0 */
  399.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  400.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  401. /* 0xD0 */
  402.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  403.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  404. /* 0xE0 */
  405.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  406.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  407. /* 0xF0 */
  408.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  409.     FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  410. };
  411.  
  412. /*
  413.  *  This procedure transparently reads and decodes the hqx input.
  414.  *  It does run length and 6 to 8 decoding.
  415.  *  As many lines as required are read to fill up buf.
  416.  * Global Input
  417.  *  buf_start
  418.  *  line_ptr
  419.  *  save_*
  420.  * Global Output
  421.  *  buf_ptr end of buffer used
  422.  *  line_ptr
  423.  *  save_*
  424.  * Internal
  425.  *  line     holds the encoded incoming ascii line.
  426.  *  buf      holds the decoding binary binary. [what the heck does this mean?]
  427.  *  buf[-1]  holds the character to be repeated for run length encoding.
  428.  */
  429. #define READING 0
  430. #define SKIPPING 1
  431. #define FIND_START_COLON 2
  432. #define FINDING_COLON 3
  433.  
  434. /*
  435.  
  436.  * It's too bad we can't look for ``(This file ..'' when hunting for a
  437.  * starting colon, but we don't emit such a line at the head of every
  438.  * segment, so we can't expect to find one, now, can we?
  439.  
  440.  * We were really too brittle when scanning, since we only
  441.  * accepted lines which were exactly 64 (HQXLINELEN) characters long.
  442.  * Fixed in version 1.83.  But allowing any length valid line
  443.  * permitted the false-match-in-mail-headers-problem for lines like "---".
  444.  * Better header skipping provided in 1.84.
  445.  
  446.  * XXX: rather than decode line inplace, it would be better
  447.  * to leave the original intact to enable better warning messages
  448.  
  449.  */
  450.  
  451. /* returns the number of bytes read */
  452. fill_hqxbuf(same_file_only)
  453.     int same_file_only;
  454. {
  455.     register ulong c, accum;
  456.     register int not_in_a_run = TRUE, state68;
  457.     register byte *fast_buf, *fast_line;
  458.     static int phase = FIND_START_COLON;
  459.     long avail;
  460.  
  461.     buf_ptr = fast_buf = buf_start;
  462.     fast_line = line_ptr;
  463.     state68 = save_state;
  464.     accum = save_accum;
  465.     if (save_run_length > 0) {
  466.         c = save_run_length;
  467.         save_run_length = 0;
  468.         goto continue_run;
  469.     }
  470.  
  471.     while (fast_buf < buf_end) {
  472.  
  473. next_char:
  474.         if ((c = *fast_line++) ISNT_LEGAL) {
  475.             if (c == DONE) {
  476.                 /* done processing the file, so get out */
  477.                 /* phase has already been set by read processing */
  478.                 break;
  479.           }
  480.  
  481.     next_line:
  482.             if (!fgets(LINE_START, MAXLINE, hqxfile)) {
  483.                 if (same_file_only) {
  484.                     avail = fast_buf - buf_ptr;
  485.                     if (avail != 0) {
  486.                         DEBUG && fprintf(debug, "DEBUG: avail: %d\n", avail);
  487.                         DEBUG && fflush(debug);
  488.                         error(
  489.                 "Premature EOF reading non-initial BinHex 4.0 header from %s",
  490.                             hqxfname);
  491.                     } else {
  492.                         /* this routine may be called many times in a row,
  493.                          * and we must ensure that we don't allow fast_line
  494.                          * to march beyond the end of line (it did), so we jam
  495.                          * fast_line to the start of the line, and salt the
  496.                          * line to mark it invalid, so a new line will be fetched.
  497.                          * Jeepers, how I hate the (il)logic of this routine!
  498.                          */
  499.                         fast_line = line_start;
  500.                         *fast_line = SKIP;
  501.                         break;
  502.                     }
  503.                     /*NOTREACHED*/
  504.                 } else if (new_in_hqx_file() == 0) {
  505.                     /*
  506.                      * Used to assume if no more input available through error or
  507.                      * exhaustion while looking for valid data then must be done.
  508.                      * But we are demand driven (now) and if called, must
  509.                      * find data.  So we don't silently exit any more.
  510.                      */
  511.                     error("Premature EOF while reading BinHex 4.0 %s", hqxfname);
  512.                 }
  513.             }
  514.             line_ptr = line_start;
  515.  
  516.             if (Debug > 1) {
  517.                       fprintf(debug, "DEBUG: input: %s", line_start);
  518.                       fflush(debug);
  519.              }
  520.  
  521.     scan_line:
  522.  
  523.             /* ensure the the entire line is valid */
  524.  
  525.             fast_line = line_ptr;
  526.             while ((*fast_line = tr68[*fast_line]) IS_LEGAL)
  527.                 fast_line++;
  528.  
  529.             /* grab the stopper */
  530.             c = *fast_line;
  531.  
  532.             /* check for validity, transition phases as required */
  533.             switch (phase) {
  534.  
  535.             case READING:
  536.             case SKIPPING:
  537.             case FINDING_COLON:
  538.                 if (SKIP == c && fast_line > line_ptr &&
  539.                     ( 0 == hqxsuspect ||
  540.                         non_suspect((char *)line_ptr, (char *)fast_line) )
  541.                 ) {
  542.                     /* the entire line is valid (again), so (resume) reading */
  543.                     /* hack: require the line to be non-empty to simplify logic */
  544.                     /* require line to non suspect [outside [mail] header] */
  545.                     phase = READING;
  546.                     break;
  547.                 }
  548.                 if (c == DONE && tr68[fast_line[1]] == SKIP) {
  549.                     /*
  550.                      * valid encoded last line, so
  551.                      * set phase for next line, and process this line --
  552.                      * we exit the fill-the-buffer loop when the terminal
  553.                      * colon is encountered during line processing
  554.                      */
  555.                     phase = FIND_START_COLON;
  556.                     break;
  557.                 }
  558.  
  559.                 /* line is not entirely valid, so do some flavor of skipping */
  560.                 phase = (phase == FINDING_COLON) ? FIND_START_COLON : SKIPPING;
  561.                 /* we're suspicious again, since we could be reading a
  562.                  * concatenated multi-segmented file */
  563.                 hqxsuspect = 1;
  564.                 goto next_line;
  565.  
  566.             case FIND_START_COLON:
  567.                 if (*line_start == DONE) {
  568.                     /* can't transition to READING
  569.                      * until know that entire line is valid
  570.                      * so transition to intermediate state
  571.                      */
  572.                     phase = FINDING_COLON;
  573.                     /* skip the initial colon */
  574.                     line_ptr++;
  575.                     goto scan_line;
  576.                 }
  577.                 goto next_line;
  578.  
  579.             }
  580.  
  581.             /* we've read in a valid line, so start processing it */
  582.             fast_line = line_ptr;
  583.             c = *fast_line++;
  584.  
  585.             /*
  586.              * Jskud 15Jul92: fix bug reported by Info-Mac Moderator Bill
  587.              * regarding case of last line just :
  588.              * The line is valid, but it has no data, so don't ingest it.
  589.              */
  590.  
  591.             if (c == DONE)
  592.                 break;
  593.  
  594.             if (Debug > 1) {
  595.                 fprintf(debug, "DEBUG: processing above line\n\n");
  596.                 fflush(debug);
  597.             }
  598.  
  599.         }
  600.  
  601.         /* Finally, we have the next 6 bits worth of data in "c" as input. */
  602.         /* Note: we use "c" as the output of this processing too */
  603.         switch (state68++) {
  604.         case 0:
  605.             accum = c;
  606.             goto next_char;
  607.         case 1:
  608.             accum = (accum << 6) | c;
  609.             c = accum >> 4;
  610.             break;
  611.         case 2:
  612.             accum = (accum << 6) | c;
  613.             c = (accum >> 2) & 0xff;
  614.             break;
  615.         case 3:
  616.             /* we avoid any miniscule optimizations here
  617.              * to maintain parallelism and clarity
  618.              * which should enhance program maintainability
  619.              */
  620.             accum = (accum << 6) | c;
  621.             c = accum & 0xff;
  622.             state68 = 0;
  623.             break;
  624.         }
  625.         if (not_in_a_run)
  626.             if (c != RUNCHAR)
  627.                 *fast_buf++ = c;
  628.             else {
  629.                 not_in_a_run = FALSE;
  630.                 goto next_char;
  631.             }
  632.         else {
  633.             /* "c" has the run total length, not just the incremental,
  634.                hence the post decrement is correct */
  635.             if (c--) {
  636.                 avail = buf_end - fast_buf;
  637.                 if (c > avail) {
  638.                     save_run_length = c - avail;
  639.                     c = avail;
  640.                 }
  641.         continue_run:
  642.                 {
  643.                     register char ch = fast_buf[-1];
  644.  
  645.                     while (c--)
  646.                         *fast_buf++ = ch;
  647.                 }
  648.  
  649.             } else
  650.                 /* handle special case of 0x9000 => 0x90 */
  651.                 *fast_buf++ = RUNCHAR;
  652.  
  653.             not_in_a_run = TRUE;
  654.         }
  655.     }
  656.  
  657. /* exit: */
  658.     avail = fast_buf - buf_ptr;
  659.     total_bytes += avail;
  660.     buf_start[-1] = fast_buf[-1];
  661.     line_ptr = fast_line;
  662.     save_state = state68;
  663.     save_accum = accum;
  664.  
  665.     return avail;
  666.  
  667. }
  668.  
  669. non_suspect(start, beyond)
  670.     char *start;
  671.     char *beyond;
  672. {
  673.     int looking_good;
  674.     int len;
  675.     register char *cp;
  676.     register char *last;
  677.     char *skip_msg = "Warning: skipping legal but suspect line in hqx file";
  678.  
  679.     /* ensure it's long enough */
  680.     len = beyond - start;
  681.     looking_good = len >= suspect_shorter;
  682.     DEBUG && fprintf(debug, "DEBUG: non_suspect: long enough: %d\n",
  683.         looking_good);
  684.     DEBUG && fflush(debug);
  685.     if (!looking_good) {
  686.         fprintf(verbose, "%s -- line too short (%d < %d)\n",
  687.             skip_msg, len, suspect_shorter);
  688.         fflush(verbose);
  689.     }
  690.  
  691.     /* ensure it's different enough */
  692.     if (suspect_same && looking_good) {
  693.         last = beyond - 1;
  694.         for (cp = start; cp < last; cp++)
  695.             if (*cp != *last) {
  696.                 break;
  697.             }
  698.         /* is different */
  699.         looking_good = cp != last;
  700.         DEBUG && fprintf(debug,
  701.             "DEBUG: non_suspect: different enough: %d\n",
  702.             looking_good);
  703.         DEBUG && fflush(debug);
  704.         if (!looking_good) {
  705.             if (*last IS_LEGAL) {
  706.                 fprintf(verbose, "%s -- all one input char ('%c')\n",
  707.                     skip_msg, tr86[*last] );
  708.             } else {
  709.                 fprintf(verbose, "%s -- all chars mapped to 0x%02x\n",
  710.                     skip_msg, *last );
  711.             }
  712.             fflush(verbose);
  713.         }
  714.     }
  715.  
  716.     hqxsuspect = !looking_good;
  717.  
  718.     return looking_good;
  719. }
  720.  
  721. new_in_hqx_file()
  722. {
  723.     extern char **hqxnames_left;
  724.     int result;
  725.     long len;
  726.  
  727.     DEBUG && fprintf(debug, "entering new_in_hqx_file ...\n");
  728.     DEBUG && fflush(debug);
  729.  
  730.     if (*hqxnames_left[0] == '\0' || *hqxnames_left[0] == '-') {
  731.         result = FALSE;
  732.         goto exit;
  733.     }
  734.  
  735.     len = strlen(*hqxnames_left);
  736.     if (len >= sizeof(hqxfname))
  737.         error("Error: specified hqxfname is too long", "");
  738.     strcpy(hqxfname, *hqxnames_left++);
  739.     /*
  740.      * we used to use freopen,
  741.      * but now suffer the slight inefficiency of close/open
  742.      * to provide info_only and consistent handling
  743.      * with good software engineering methods
  744.      */
  745.     mclose(&hqxfile, "hqxfile");
  746.     hqxfile = mopen(hqxfname, ".hqx", "r");
  747.     hqxsuspect = 1;
  748.     (void)fgets(LINE_START, MAXLINE, hqxfile);
  749.     result = TRUE;
  750.  
  751. exit:
  752.     if (result == TRUE)
  753.         DEBUG && fprintf(debug, "... opened %s\n", hqxfname);
  754.     else
  755.         DEBUG && fprintf(debug, "... nothing to open\n");
  756.     DEBUG && fflush(debug);
  757.  
  758.     return result;
  759. }
  760.  
  761. /*
  762.  *  This procedure transparently encodes and writes the hqx output.  
  763.  *  It does run length and 8 to 6 encoding.
  764.  */
  765. empty_hqxbuf()
  766. {
  767.     register ulong c, accum, last_c;
  768.     register byte *fast_buf, *fast_line;
  769.     register int state86, dont_look_for_runs = FALSE, run_length;
  770.     extern int maxlines;
  771.  
  772.     run_length = save_run_length;
  773.     last_c = buf_start[-1];
  774.     fast_buf = buf_start;
  775.     fast_line = line_ptr;
  776.     state86 = save_state;
  777.     accum = save_accum;
  778.     while (fast_buf < buf_ptr) {
  779.         c = *fast_buf++;
  780.         if (dont_look_for_runs)
  781.             dont_look_for_runs = FALSE;
  782.         else if (last_c == c && run_length < MAXRUN) {
  783.             run_length++;
  784.             continue;
  785.         } else {
  786.             if (run_length > 1) {
  787.                 --fast_buf;
  788.                 if (run_length == 2 && last_c != RUNCHAR)
  789.                     c = last_c;
  790.                 else {
  791.                     c = RUNCHAR;
  792.                     *--fast_buf = run_length;
  793.                     dont_look_for_runs = TRUE;
  794.                 }
  795.                 run_length = 1;
  796.             } else
  797.                 last_c = c;
  798.             if (c == RUNCHAR && !dont_look_for_runs) {
  799.                 *--fast_buf = 0;
  800.                 dont_look_for_runs = TRUE;
  801.             }
  802.         }
  803.  
  804.         if (fast_line == line_end) {
  805.             if (line_count++ == maxlines)
  806.                 new_out_hqx_file();
  807.             fputs(LINE_START, hqxfile);
  808.             fast_line = line_start;
  809.         }
  810.         switch (state86++) {
  811.         case 0:
  812.             *fast_line++ = tr86[c >> 2];
  813.             accum = (c << 4) & 0x3f;
  814.             break;
  815.         case 1:
  816.             *fast_line++ = tr86[(c >> 4) | accum];
  817.             accum = (c << 2) & 0x3f;
  818.             break;
  819.         case 2:
  820.             *fast_line++ = tr86[(c >> 6) | accum];
  821.             if (fast_line == line_end) {
  822.                 if (line_count++ == maxlines)
  823.                     new_out_hqx_file();
  824.                 fputs(LINE_START, hqxfile);
  825.                 fast_line = line_start;
  826.             }
  827.             *fast_line++ = tr86[c & 0x3f];
  828.             state86 = 0;
  829.             break;
  830.         }
  831.     }
  832.     save_run_length = run_length;
  833.     buf_start[-1] = last_c;
  834.     buf_ptr = buf_start;
  835.     line_ptr = fast_line;
  836.     save_state = state86;
  837.     save_accum = accum;
  838. }
  839.  
  840. new_out_hqx_file()
  841. {
  842.     char filename[SYSNAMELEN + 7];
  843.     extern int maxlines;
  844.  
  845.     fprintf(hqxfile, "<<< End of Part %2d >>>\n", file_count);
  846.     mclose(&hqxfile, "hqxfile");
  847.     file_count++;
  848.     if (maxlines)
  849.         sprintf(filename, "%s%02d.hqx", hqxfname, file_count);
  850.     else
  851.         sprintf(filename, "%s.hqx", hqxfname);
  852.     hqxfile = mopen(filename, "", "w");
  853.     if (file_count > 1)
  854.         fprintf(hqxfile, "<<< Start of Part %2d >>>\n", file_count);
  855.     else
  856.         fprintf(hqxfile, "(This file must be converted with BinHex 4.0)\n\n");
  857.     line_count = 3;
  858. }
  859.  
  860. check_hqx_crc(calc_crc, msg, name)
  861.     word calc_crc;
  862.     char msg[], name[];
  863.  
  864. {
  865.     word read_crc;
  866.  
  867.     if (buf_ptr >= buf_end)
  868.         (void)fill_hqxbuf(0);
  869.     read_crc = *buf_ptr++ << 8;
  870.     if (buf_ptr >= buf_end)
  871.         (void)fill_hqxbuf(0);
  872.     read_crc |= *buf_ptr++;
  873.     if (read_crc != calc_crc)
  874.         error(msg, name);
  875. }
  876.  
  877. write_hqx_crc(calc_crc)
  878.     word calc_crc;
  879. {
  880.     if (buf_ptr == buf_end)
  881.         empty_hqxbuf();
  882.     *buf_ptr++ = calc_crc >> 8;
  883.     if (buf_ptr == buf_end)
  884.         empty_hqxbuf();
  885.     *buf_ptr++ = calc_crc;
  886. }
  887.  
  888. un_hqx(unpit_flag)
  889.     int unpit_flag;
  890. {
  891.     char type[5];    /* stuff EOS */
  892.     ulong hqx_datalen, hqx_rsrclen;
  893.     word un_pit();
  894.     int unpitting, bytes_read;
  895.     word calc_crc;
  896.     extern char **hqxnames_left;
  897.     int same_file_only = 0;
  898.     int active;
  899.  
  900.     /* we read EOF on this to transision, so the stream must be valid */
  901.     hqxfile = devnull;
  902.     line_end = line_start + HQXLINELEN;
  903.     buf_end = buf_start + HQXBUFLEN;
  904.  
  905.     while (1) {
  906.         total_bytes = 0;
  907.         line_ptr = line_start;
  908.         /* ensure that the line buffer is considered empty */
  909.         /* why we use SKIP and not newline, I'm not sure */
  910.         line_ptr[0] = SKIP;
  911.         save_state = 0;
  912.         save_run_length = 0;
  913.  
  914.         active = hqx_to_bin_hdr(type, &hqx_datalen, &hqx_rsrclen, same_file_only);
  915.         if (active == 0)
  916.             break;
  917.         same_file_only = 1;
  918.         type[4] = 0; /* set EOS */
  919.  
  920.         unpitting = unpit_flag && !strcmp(type, "PIT ");
  921.         DEBUG && fprintf(debug,
  922.             "DEBUG: unpit_flag=%d type=%s unpitting=%d\n",
  923.             unpit_flag, type, unpitting);
  924.         DEBUG && fflush(debug);
  925.         if (unpitting) {
  926.             mclose(&binfile, "binfile");
  927.             /* Do not unlink files we did not open */
  928.            if (!info_only)
  929.                 unlink(binfname);
  930.             bytes_read = total_bytes - (buf_end - buf_ptr);
  931.             calc_crc = un_pit();
  932.             bytes_read = total_bytes - (buf_end - buf_ptr) - bytes_read;
  933.             if (bytes_read != hqx_datalen) {
  934.                 fprintf(verbose,
  935.                     "Warning - Extraneous characters ignored in %s\n", binfname);
  936.                 fflush(verbose);
  937.             }
  938.         } else {
  939.             calc_crc = hqx_to_bin_fork(hqx_datalen);
  940.         }
  941.         check_hqx_crc(calc_crc, "File data CRC mismatch in %s", binfname);
  942.  
  943.         calc_crc = hqx_to_bin_fork(hqx_rsrclen);
  944.         check_hqx_crc(calc_crc, "File rsrc CRC mismatch in %s", binfname);
  945.  
  946.         if (!unpitting) {
  947.             mclose(&binfile, "binfile");
  948.         }
  949.  
  950.     }
  951.     mclose(&hqxfile, "hqxfile");
  952. }
  953.  
  954. re_hqx()
  955. {
  956.     word calc_crc;
  957.     ulong hqx_datalen, hqx_rsrclen;
  958.     extern char **hqxnames_left;
  959.     extern int maxlines;
  960.  
  961.     line_end = line_start + HQXLINELEN;
  962.     buf_end = buf_start + HQXBUFLEN;
  963.     while (*hqxnames_left[0] != '-') {
  964.         /* we write the trailer, so the stream must be valid */
  965.         hqxfile = devnull;
  966.  
  967.         /*
  968.        * We use the trick of setting our line_count at the limit to
  969.        * force an immediate transition on overflow.
  970.          */
  971.         line_count = maxlines;
  972.  
  973.         file_count = 0;
  974.         line_ptr = line_start;
  975.         *line_ptr++ = ':';
  976.         strcpy((char*)line_end, "\n");
  977.         buf_ptr = buf_start;
  978.         save_state = 0;
  979.         save_run_length = 1;
  980.  
  981.         bin_to_hqx_hdr(&hqx_datalen, &hqx_rsrclen);    /* calculates hqxfname */
  982.  
  983.         /*
  984.        * Now that we have hqxfname, start the new file if
  985.          * not yet started.  We no longer wait until the output
  986.          * buffer overflows, since empty files with short names didn't overflow!
  987.          */
  988.  
  989.         if (file_count == 0)
  990.             new_out_hqx_file();
  991.  
  992.         calc_crc = bin_to_hqx_fork(hqx_datalen);
  993.         write_hqx_crc(calc_crc);
  994.  
  995.         calc_crc = bin_to_hqx_fork(hqx_rsrclen);
  996.         write_hqx_crc(calc_crc);
  997.         /*
  998.        * To end a run and to get the last stray bits,
  999.          * temporarily add a char.
  1000.          */
  1001.         *buf_ptr = !buf_ptr[-1];
  1002.         buf_ptr++;
  1003.         empty_hqxbuf();
  1004.         /* now toss any extra output character generated */
  1005.         if (save_state != 2)
  1006.             --line_ptr;
  1007.  
  1008.         /* if we're at the end of the line now, write it out */
  1009.         if (line_ptr == line_end) {
  1010.             fputs(LINE_START, hqxfile);
  1011.             line_ptr = line_start;
  1012.         }
  1013.  
  1014.         /* paste the trailing colon onto the end of the line */
  1015.         /* recall that line_ptr points into the line, not at the line */
  1016.         strcpy((char*)line_ptr, ":\n");
  1017.         
  1018.         /* and flush the output buffer */
  1019.         fputs(LINE_START, hqxfile);
  1020.  
  1021.         mclose(&hqxfile, "hqxfile");
  1022.         mclose(&binfile, "binfile");
  1023.  
  1024.     }
  1025. }
  1026.  
  1027.