home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / util / unix / mcvert18.sha / hqxify.c next >
Encoding:
C/C++ Source or Header  |  1992-11-05  |  24.1 KB  |  883 lines

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