home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / unix / xbinshar < prev   
Text File  |  1989-04-10  |  24KB  |  1,082 lines

  1. #!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
  2. # shar:    Shell Archiver
  3. #    Run the following text with /bin/sh to create:
  4. #    README #    xbin.c #    xbin.l 
  5. echo shar: extracting README
  6. cat - << \SHAR_EOF > README
  7. This is version 2.3 of xbin.  The major changes include
  8. perfomance improvements from Dan LaLiberte of UIUC, fixes
  9. for 16-bit machines from Jim Budler of AMD, and a fix for
  10. a bug in the run-length encoding code.
  11.  
  12. This version of "xbin" can handle all three BinHex formats
  13. (so far).  Thanks to Darin Adler at TMQ Software for providing
  14. the code to compute and check the CRC values for all three formats.
  15. (There are no plans to support binhex5.0, as its use of binary
  16. encoding makes it useless for sending programs through e-mail).
  17.  
  18. Other new features include "list" and "verbose" modes, the
  19. ability to convert several binhex files at one time, the ability
  20. to read standard input, somewhat better error handling, and a
  21. manual page.
  22.  
  23. Any extraneous mail or news headers are ignored, but xbin relies
  24. on finding a line which starts with "(This file" to know when
  25. the header ends and the good stuff begins.  You can add one
  26. of these by hand if it's been lost.
  27.  
  28. To compile it on USG systems, type:
  29.     cc -o xbin xbin.c
  30.  
  31. or on Berkeley systems:
  32.     cc -o xbin xbin.c -DBSD
  33.  
  34. As usual, please report any problems, suggestions, or
  35. improvements to me.
  36.  
  37.     Dave Johnson
  38.     Brown University Computer Science
  39.     ddj%brown@csnet-relay.ARPA
  40.     {ihnp4,decvax,allegra,ulysses,linus}!brunix!ddj
  41.  
  42. ===================
  43. Here's an informal description of the HQX format as I understand it:
  44. -----
  45. The first and last characters are each a ':'.  After the first ':',
  46. the rest of the file is just string of 6 bit encoded characters.
  47. All newlines and carriage returns are to be ignored.
  48.  
  49. The tricky part is that there are holes in the translation string
  50. so you have to look up each file character to get its binary 6 bit
  51. value.  I found the string by looking at a hex dump of BinHex:
  52.  
  53.     !"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr
  54.  
  55. I can't see how this aids or abets any kind of error recovery, but
  56. if you ran into a char not in the list, you would know something's
  57. wrong and give up.
  58.  
  59. There is some run length encoding, where the character to be repeated
  60. is followed by a 0x90 byte then the repeat count.  For example, ff9004
  61. means repeat 0xff 4 times.  The special case of a repeat count of zero
  62. means it's not a run, but a literal 0x90.  2b9000 => 2b90.
  63.  
  64. *** Note: the 9000 can be followed by a run, which means to repeat the
  65. 0x90 (not the character previous to that).  That is, 2090009003 means
  66. a 0x20 followed by 3 0x90's.
  67.  
  68. Once you've turned the 6 bit chars into 8, you can parse the header.
  69. The header format consists of a one byte name length, then the mac
  70. file name, then a null.  The rest of the header is 20 bytes long,
  71. and contains the usual file type, creator/author, file flags, data
  72. and resource lengths, and the two byte crc value for the header.
  73.  
  74. The data fork and resource fork contents follow in that order.
  75. There is a two byte file crc at the end of each fork.  If a fork
  76. is empty, there will be no bytes of contents and the checksum
  77. will be two bytes of zero.
  78.  
  79. So the decoded data between the first and last ':' looks like:
  80.  
  81.      1       n       4    4    2    4    4   2    (length)
  82.     +-+---------+-+----+----+----+----+----+--+
  83.     |n| name... |0|TYPE|AUTH|FLAG|DLEN|RLEN|HC|    (contents)
  84.     +-+---------+-+----+----+----+----+----+--+
  85.  
  86.             DLEN             2    (length)
  87.     +--------------------------------------+--+
  88.     |    DATA FORK               |DC|    (contents)
  89.     +--------------------------------------+--+
  90.  
  91.             RLEN             2    (length)
  92.     +--------------------------------------+--+
  93.     |    RESOURCE FORK               |RC|    (contents)
  94.     +--------------------------------------+--+
  95.  
  96. ------
  97. SHAR_EOF
  98. echo shar: extracting xbin.c
  99. cat - << \SHAR_EOF > xbin.c
  100. #ifndef lint
  101. static char version[] = "xbin.c Version 2.3 09/30/85";
  102. #endif lint
  103.  
  104. #include <stdio.h>
  105. #include <sys/types.h>
  106. #include <sys/stat.h>
  107. #include <sys/dir.h>
  108.  
  109. #ifdef MAXNAMLEN    /* 4.2 BSD */
  110. #define FNAMELEN MAXNAMLEN
  111. #else
  112. #define FNAMELEN DIRSIZ
  113. #endif
  114.  
  115. #ifdef BSD
  116. #include <sys/time.h>
  117. #include <sys/timeb.h>
  118. #define search_last rindex
  119. extern char *rindex();
  120. #else
  121. #include <time.h>
  122. extern long timezone;
  123. #define search_last strrchr
  124. extern char *strrchr();
  125. #endif
  126.  
  127. /* Mac time of 00:00:00 GMT, Jan 1, 1970 */
  128. #define TIMEDIFF 0x7c25b080
  129.  
  130. #define DATABYTES 128
  131.  
  132. #define BYTEMASK 0xff
  133. #define BYTEBIT 0x100
  134. #define WORDMASK 0xffff
  135. #define WORDBIT 0x10000
  136.  
  137. #define NAMEBYTES 63
  138. #define H_NLENOFF 1
  139. #define H_NAMEOFF 2
  140.  
  141. /* 65 <-> 80 is the FInfo structure */
  142. #define H_TYPEOFF 65
  143. #define H_AUTHOFF 69
  144. #define H_FLAGOFF 73
  145.  
  146. #define H_LOCKOFF 81
  147. #define H_DLENOFF 83
  148. #define H_RLENOFF 87
  149. #define H_CTIMOFF 91
  150. #define H_MTIMOFF 95
  151.  
  152. #define H_OLD_DLENOFF 81
  153. #define H_OLD_RLENOFF 85
  154.  
  155. #define F_BUNDLE 0x2000
  156. #define F_LOCKED 0x8000
  157.  
  158. struct macheader {
  159.     char m_name[NAMEBYTES+1];
  160.     char m_type[4];
  161.     char m_author[4];
  162.     short m_flags;
  163.     long m_datalen;
  164.     long m_rsrclen;
  165.     long m_createtime;
  166.     long m_modifytime;
  167. } mh;
  168.  
  169. struct filenames {
  170.     char f_info[256];
  171.     char f_data[256];
  172.     char f_rsrc[256];
  173. } files;
  174.  
  175. int pre_beta;    /* options */
  176. int listmode;
  177. int verbose;
  178.  
  179. int compressed;    /* state variables */
  180. int qformat;
  181. FILE *ifp;
  182.  
  183. /*
  184.  * xbin -- unpack BinHex format file into suitable
  185.  * format for downloading with macput
  186.  * Dave Johnson, Brown University Computer Science
  187.  *
  188.  * (c) 1984 Brown University
  189.  * may be used but not sold without permission
  190.  *
  191.  * created ddj 12/16/84
  192.  * revised ddj 03/10/85 -- version 4.0 compatibility, other minor mods
  193.  * revised ddj 03/11/85 -- strip LOCKED bit from m_flags
  194.  * revised ahm 03/12/85 -- System V compatibility
  195.  * revised dba 03/16/85 -- (Darin Adler, TMQ Software)  4.0 EOF fixed,
  196.  *               4.0 checksum added
  197.  * revised ddj 03/17/85 -- extend new features to older formats: -l, stdin
  198.  * revised ddj 03/24/85 -- check for filename truncation, allow multiple files
  199.  * revised ddj 03/26/85 -- fixed USG botches, many problems w/multiple files
  200.  * revised jcb 03/30/85 -- (Jim Budler, amdcad!jimb), revised for compatibility
  201.  *               with 16-bit int machines
  202.  * revised dl  06/16/85 -- (Dan LaLiberte, liberte@uiucdcs) character
  203.  *               translation speedup
  204.  * revised ddj 09/30/85 -- fixed problem with run of RUNCHAR
  205.  */
  206. char usage[] = "usage: \"xbin [-v] [-l] [-o] [-n name] [-] filename\"\n";
  207.  
  208. main(ac, av)
  209. char **av;
  210. {
  211.     char *filename, *macname;
  212.  
  213.     filename = ""; macname = "";
  214.     ac--; av++;
  215.     while (ac) {
  216.         if (av[0][0] == '-') {
  217.             switch (av[0][1]) {
  218.             case '\0':
  219.                 filename = "-";
  220.                 break;
  221.             case 'v':
  222.                 verbose++;
  223.                 break;
  224.             case 'l':
  225.                 listmode++;
  226.                 break;
  227.             case 'o':
  228.                 pre_beta++;
  229.                 break;
  230.             case 'n':
  231.                 if (ac > 1) {
  232.                     ac--; av++;
  233.                     macname = av[0];
  234.                     filename = "";
  235.                     break;
  236.                 }
  237.                 else
  238.                     goto bad_usage;
  239.             default:
  240.                 goto bad_usage;
  241.             }
  242.         }
  243.         else
  244.             filename = av[0];
  245.         if (filename[0] != '\0') {
  246.             setup_files(filename, macname);
  247.             if (listmode) {
  248.                 print_header();
  249.             }
  250.             else {
  251.                 process_forks();
  252.                 /* now that we know the size of the forks */
  253.                 forge_info();
  254.             }
  255.             if (ifp != stdin)
  256.                 fclose(ifp);
  257.             macname = "";
  258.             ifp = NULL;        /* reset state */
  259.             qformat = 0;
  260.             compressed = 0;
  261.         }
  262.         ac--; av++;
  263.     }
  264.     if (*filename == '\0') {
  265. bad_usage:
  266.         fprintf(stderr, usage);
  267.         exit(1);
  268.     }
  269. }
  270.  
  271. static char *extensions[] = {
  272.     ".hqx",
  273.     ".hcx",
  274.     ".hex",
  275.     "",
  276.     NULL
  277. };
  278.  
  279. setup_files(filename, macname)
  280. char *filename;        /* input file name -- extension optional */
  281. char *macname;        /* name to use on the mac side of things */
  282. {
  283.     char namebuf[256], *np;
  284.     char **ep;
  285.     int n;
  286.     struct stat stbuf;
  287.     long curtime;
  288.  
  289.     if (filename[0] == '-') {
  290.         ifp = stdin;
  291.         filename = "stdin";
  292.     }
  293.     else {
  294.         /* find input file and open it */
  295.         for (ep = extensions; *ep != NULL; ep++) {
  296.             sprintf(namebuf, "%s%s", filename, *ep);
  297.             if (stat(namebuf, &stbuf) == 0)
  298.                 break;
  299.         }
  300.         if (*ep == NULL) {
  301.             perror(namebuf);
  302.             exit(-1);
  303.         }
  304.         ifp = fopen(namebuf, "r");
  305.         if (ifp == NULL) {
  306.             perror(namebuf);
  307.             exit(-1);
  308.         }
  309.     }
  310.     if (ifp == stdin) {
  311.         curtime = time(0);
  312.         mh.m_createtime = curtime;
  313.         mh.m_modifytime = curtime;
  314.     }
  315.     else {
  316.         mh.m_createtime = stbuf.st_mtime;
  317.         mh.m_modifytime = stbuf.st_mtime;
  318.     }
  319.     if (listmode || verbose) {
  320.         fprintf(stderr, "%s %s%s",
  321.             listmode ? "\nListing" : "Converting",
  322.             namebuf, listmode ? ":\n" : " ");
  323.     }
  324.  
  325.     qformat = find_header(); /* eat mailer header &cetera, intuit format */
  326.  
  327.     if (qformat)
  328.         do_q_header(macname);
  329.     else
  330.         do_o_header(macname, filename);
  331.  
  332.     /* make sure host file name doesn't get truncated beyond recognition */
  333.     n = strlen(mh.m_name);
  334.     if (n > FNAMELEN - 2)
  335.         n = FNAMELEN - 2;
  336.     strncpy(namebuf, mh.m_name, n);
  337.     namebuf[n] = '\0';
  338.  
  339.     /* get rid of troublesome characters */
  340.     for (np = namebuf; *np; np++)
  341.         if (*np == ' ' || *np == '/')
  342.             *np = '_';
  343.  
  344.     sprintf(files.f_data, "%s.data", namebuf);
  345.     sprintf(files.f_rsrc, "%s.rsrc", namebuf);
  346.     sprintf(files.f_info, "%s.info", namebuf);
  347.     if (verbose)
  348.         fprintf(stderr, "==> %s.{info,data,rsrc}\n", namebuf);
  349. }
  350.  
  351. /* print out header information in human-readable format */
  352. print_header()
  353. {
  354.     char *ctime();
  355.  
  356.     printf("macname: %s\n", mh.m_name);
  357.     printf("filetype: %.4s, ", mh.m_type);
  358.     printf("author: %.4s, ", mh.m_author);
  359.     printf("flags: 0x%x\n", mh.m_flags);
  360.     if (qformat) {
  361.         printf("data length: %ld, ", mh.m_datalen);
  362.         printf("rsrc length: %ld\n", mh.m_rsrclen);
  363.     }
  364.     if (!pre_beta) {
  365.         printf("create time: %s", ctime(&mh.m_createtime));
  366.     }
  367. }
  368.  
  369. process_forks()
  370. {
  371.     if (qformat) {
  372.         /* read data and resource forks of .hqx file */
  373.         do_q_fork(files.f_data, mh.m_datalen);
  374.         do_q_fork(files.f_rsrc, mh.m_rsrclen);
  375.     }
  376.     else
  377.         do_o_forks();
  378. }
  379.  
  380. /* write out .info file from information in the mh structure */
  381. forge_info()
  382. {
  383.     static char buf[DATABYTES];
  384.     char *np;
  385.     FILE *fp;
  386.     int n;
  387.     long tdiff;
  388.     struct tm *tp;
  389. #ifdef BSD
  390.     struct timeb tbuf;
  391. #else
  392.     long bs;
  393. #endif
  394.  
  395.     for (np = mh.m_name; *np; np++)
  396.         if (*np == '_') *np = ' ';
  397.  
  398.     buf[H_NLENOFF] = n = np - mh.m_name;
  399.     strncpy(buf + H_NAMEOFF, mh.m_name, n);
  400.     strncpy(buf + H_TYPEOFF, mh.m_type, 4);
  401.     strncpy(buf + H_AUTHOFF, mh.m_author, 4);
  402.     put2(buf + H_FLAGOFF, mh.m_flags & ~F_LOCKED);
  403.     if (pre_beta) {
  404.         put4(buf + H_OLD_DLENOFF, mh.m_datalen);
  405.         put4(buf + H_OLD_RLENOFF, mh.m_rsrclen);
  406.     }
  407.     else {
  408.         put4(buf + H_DLENOFF, mh.m_datalen);
  409.         put4(buf + H_RLENOFF, mh.m_rsrclen);
  410.  
  411.         /* convert unix file time to mac time format */
  412. #ifdef BSD
  413.         ftime(&tbuf);
  414.         tp = localtime(&tbuf.time);
  415.         tdiff = TIMEDIFF - tbuf.timezone * 60;
  416.         if (tp->tm_isdst)
  417.             tdiff += 60 * 60;
  418. #else
  419.         /* I hope this is right! -andy */
  420.         time(&bs);
  421.         tp = localtime(&bs);
  422.         tdiff = TIMEDIFF - timezone;
  423.         if (tp->tm_isdst)
  424.             tdiff += 60 * 60;
  425. #endif
  426.         put4(buf + H_CTIMOFF, mh.m_createtime + tdiff);
  427.         put4(buf + H_MTIMOFF, mh.m_modifytime + tdiff);
  428.     }
  429.     fp = fopen(files.f_info, "w");
  430.     if (fp == NULL) {
  431.         perror("info file");
  432.         exit(-1);
  433.     }
  434.     fwrite(buf, 1, DATABYTES, fp);
  435.     fclose(fp);
  436. }
  437.  
  438. /* eat characters until header detected, return which format */
  439. find_header()
  440. {
  441.     int c, at_bol;
  442.     char ibuf[BUFSIZ];
  443.  
  444.     /* look for "(This file ...)" line */
  445.     while (fgets(ibuf, BUFSIZ, ifp) != NULL) {
  446.         if (strncmp(ibuf, "(This file", 10) == 0)
  447.             break;
  448.     }
  449.     at_bol = 1;
  450.     while ((c = getc(ifp)) != EOF) {
  451.         switch (c) {
  452.         case '\n':
  453.         case '\r':
  454.             at_bol = 1;
  455.             break;
  456.         case ':':
  457.             if (at_bol)    /* q format */
  458.                 return 1;
  459.             break;
  460.         case '#':
  461.             if (at_bol) {    /* old format */
  462.                 ungetc(c, ifp);
  463.                 return 0;
  464.             }
  465.             break;
  466.         default:
  467.             at_bol = 0;
  468.             break;
  469.         }
  470.     }
  471.  
  472.     fprintf(stderr, "unexpected EOF\n");
  473.     exit(2);
  474.     /* NOTREACHED */
  475. }
  476.  
  477. static unsigned int crc;
  478.  
  479. short get2q();
  480. long get4q();
  481.  
  482. /* read header of .hqx file */
  483. do_q_header(macname)
  484. char *macname;
  485. {
  486.     char namebuf[256];        /* big enough for both att & bsd */
  487.     int n;
  488.     unsigned int calc_crc, file_crc;
  489.  
  490.     crc = 0;            /* compute a crc for the header */
  491.     q_init();            /* reset static variables */
  492.  
  493.     n = getq();            /* namelength */
  494.     n++;                /* must read trailing null also */
  495.     getqbuf(namebuf, n);        /* read name */
  496.     if (macname[0] == '\0')
  497.         macname = namebuf;
  498.  
  499.     n = strlen(macname);
  500.     if (n > NAMEBYTES)
  501.         n = NAMEBYTES;
  502.     strncpy(mh.m_name, macname, n);
  503.     mh.m_name[n] = '\0';
  504.  
  505.     getqbuf(mh.m_type, 4);
  506.     getqbuf(mh.m_author, 4);
  507.     mh.m_flags = get2q();
  508.     mh.m_datalen = get4q();
  509.     mh.m_rsrclen = get4q();
  510.  
  511.     comp_q_crc(0);
  512.     comp_q_crc(0);
  513.     calc_crc = crc;
  514.     file_crc = get2q();
  515.     verify_crc(calc_crc, file_crc);
  516. }
  517.  
  518. do_q_fork(fname, len)
  519. char *fname;
  520. register long len;
  521. {
  522.     FILE *outf;
  523.     register int c, i;
  524.     unsigned int calc_crc, file_crc;
  525.  
  526.     outf = fopen(fname, "w");
  527.     if (outf == NULL) {
  528.         perror(fname);
  529.         exit(-1);
  530.     }
  531.  
  532.     crc = 0;    /* compute a crc for a fork */
  533.  
  534.     if (len)
  535.         for (i = 0; i < len; i++) {
  536.             if ((c = getq()) == EOF) {
  537.                 fprintf(stderr, "unexpected EOF\n");
  538.                 exit(2);
  539.             }
  540.             putc(c, outf);
  541.         }
  542.  
  543.     comp_q_crc(0);
  544.     comp_q_crc(0);
  545.     calc_crc = crc;
  546.     file_crc = get2q();
  547.     verify_crc(calc_crc, file_crc);
  548.     fclose(outf);
  549. }
  550.  
  551. /* verify_crc(); -- check if crc's check out */
  552. verify_crc(calc_crc, file_crc)
  553. unsigned int calc_crc, file_crc;
  554. {
  555.     calc_crc &= WORDMASK;
  556.     file_crc &= WORDMASK;
  557.  
  558.     if (calc_crc != file_crc) {
  559.         fprintf(stderr, "CRC error\n---------\n");
  560.         fprintf(stderr, "CRC in file:\t0x%x\n", file_crc);
  561.         fprintf(stderr, "calculated CRC:\t0x%x\n", calc_crc);
  562.         exit(3);
  563.     }
  564. }
  565.  
  566. static int eof;
  567. static char obuf[3];
  568. static char *op, *oend;
  569.  
  570. /* initialize static variables for q format input */
  571. q_init()
  572. {
  573.     eof = 0;
  574.     op = obuf;
  575.     oend = obuf + sizeof obuf;
  576. }
  577.  
  578. /* get2q(); q format -- read 2 bytes from input, return short */
  579. short
  580. get2q()
  581. {
  582.     register int c;
  583.     short value = 0;
  584.  
  585.     c = getq();
  586.     value = (c & BYTEMASK) << 8;
  587.     c = getq();
  588.     value |= (c & BYTEMASK);
  589.  
  590.     return value;
  591. }
  592.  
  593. /* get4q(); q format -- read 4 bytes from input, return long */
  594. long
  595. get4q()
  596. {
  597.     register int c, i;
  598.     long value = 0L;
  599.  
  600.     for (i = 0; i < 4; i++) {
  601.         c = getq();
  602.         value <<= 8;
  603.         value |= (c & BYTEMASK);
  604.     }
  605.     return value;
  606. }
  607.  
  608. /* getqbuf(); q format -- read n characters from input into buf */
  609. /*        All or nothing -- no partial buffer allowed */
  610. getqbuf(buf, n)
  611. register char *buf;
  612. register int n;
  613. {
  614.     register int c, i;
  615.  
  616.     for (i = 0; i < n; i++) {
  617.         if ((c = getq()) == EOF)
  618.             return EOF;
  619.         *buf++ = c;
  620.     }
  621.     return 0;
  622. }
  623.  
  624. #define RUNCHAR 0x90
  625.  
  626. /* q format -- return one byte per call, keeping track of run codes */
  627. getq()
  628. {
  629.     register int c;
  630.  
  631.     if ((c = getq_nocrc()) == EOF)
  632.         return EOF;
  633.     comp_q_crc((unsigned)c);
  634.     return c;
  635. }
  636.  
  637. getq_nocrc()
  638. {
  639.     static int rep, lastc;
  640.     int c;
  641.  
  642.     if (rep) {
  643.         rep--;
  644.         return lastc;
  645.     }
  646.     if ((c = getq_raw()) == EOF) {
  647.         return EOF;
  648.     }
  649.     if (c == RUNCHAR) {
  650.         if ((rep = getq_raw()) == EOF)
  651.             return EOF;
  652.         if (rep != 0) {
  653.             /* already returned one, about to return another */
  654.             rep -= 2;
  655.             return lastc;
  656.         }
  657.         else {
  658.             lastc = RUNCHAR;
  659.             return RUNCHAR;
  660.         }
  661.     }
  662.     else {
  663.         lastc = c;
  664.         return c;
  665.     }
  666. }
  667.  
  668. /* q format -- return next 8 bits from file without interpreting run codes */
  669. getq_raw()
  670. {
  671.     char ibuf[4];
  672.     register char *ip = ibuf, *iend = ibuf + sizeof ibuf;
  673.     int c;
  674.  
  675.     if (op == obuf) {
  676.         for (ip = ibuf; ip < iend; ip++) {
  677.             if ((c = get6bits()) == EOF)
  678.                 if (ip <= &ibuf[1])
  679.                     return EOF;
  680.                 else if (ip == &ibuf[2])
  681.                     eof = 1;
  682.                 else
  683.                     eof = 2;
  684.             *ip = c;
  685.         }
  686.         obuf[0] = (ibuf[0] << 2 | ibuf[1] >> 4);
  687.         obuf[1] = (ibuf[1] << 4 | ibuf[2] >> 2);
  688.         obuf[2] = (ibuf[2] << 6 | ibuf[3]);
  689.     }
  690.     if ((eof) & (op >= &obuf[eof]))
  691.         return EOF;
  692.     c = *op++;
  693.     if (op >= oend)
  694.         op = obuf;
  695.     return (c & BYTEMASK);
  696. }
  697.  
  698. /*
  699. char tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
  700.          0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
  701.          0                1               2               3 
  702. trlookup is used to translate by direct lookup.  The input character
  703. is an index into trlookup.  If the result is 0xFF, a bad char has been read.
  704. Added by:  Dan LaLiberte, liberte@uiucdcs.Uiuc.ARPA, ihnp4!uiucdcs!liberte
  705. */
  706. char trlookup[83] = {     0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
  707.             0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
  708.             0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
  709.             0x14, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  710.             0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
  711.             0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
  712.             0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
  713.             0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
  714.             0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
  715.             0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
  716.             0x3D, 0x3E, 0x3F };
  717.  
  718. /* q format -- decode one byte into 6 bit binary */
  719. get6bits()
  720. {
  721.     register int c;
  722.     register int tc;
  723.  
  724.     while (1) {
  725.         c = getc(ifp);
  726.         switch (c) {
  727.         case '\n':
  728.         case '\r':
  729.             continue;
  730.         case ':':
  731.         case EOF:
  732.             return EOF;
  733.         default:
  734.              tc = ((c-' ') < 83) ? trlookup[c-' '] : 0xff;
  735. /*            fprintf(stderr, "c = '%c'  tc = %4x\n", c, tc); */
  736.             if (tc != 0xff)
  737.                 return (tc);
  738.             fprintf(stderr, "bad char: '%c'\n", c);
  739.             return EOF;
  740.         }
  741.     }
  742. }
  743.  
  744.  
  745. #define CRCCONSTANT 0x1021
  746.  
  747. comp_q_crc(c)
  748. register unsigned int c;
  749. {
  750.     register int i;
  751.     register unsigned long temp = crc;
  752.  
  753.     for (i=0; i<8; i++) {
  754.         c <<= 1;
  755.         if ((temp <<= 1) & WORDBIT)
  756.             temp = (temp & WORDMASK) ^ CRCCONSTANT;
  757.         temp ^= (c >> 8);
  758.         c &= BYTEMASK;
  759.     }
  760.     crc = temp;
  761. }
  762.  
  763. /* old format -- process .hex and .hcx files */
  764. do_o_header(macname, filename)
  765. char *macname, *filename;
  766. {
  767.     char namebuf[256];        /* big enough for both att & bsd */
  768.     char ibuf[BUFSIZ];
  769.     int n;
  770.  
  771.     /* set up name for output files */
  772.     if (macname[0] == '\0') {
  773.         strcpy(namebuf, filename);
  774.  
  775.         /* strip directories */
  776.         macname = search_last(namebuf, '/');
  777.         if (macname == NULL)
  778.             macname = namebuf;
  779.         else
  780.             macname++;
  781.  
  782.         /* strip extension */
  783.         n = strlen(macname);
  784.         if (n > 4) {
  785.             n -= 4;
  786.             if (macname[n] == '.' && macname[n+1] == 'h'
  787.                         && macname[n+3] == 'x')
  788.                 macname[n] = '\0';
  789.         }
  790.     }
  791.     n = strlen(macname);
  792.     if (n > NAMEBYTES)
  793.         n = NAMEBYTES;
  794.     strncpy(mh.m_name, macname, n);
  795.     mh.m_name[n] = '\0';
  796.  
  797.     /* read "#TYPEAUTH$flag"  line */
  798.     if (fgets(ibuf, BUFSIZ, ifp) == NULL) {
  799.         fprintf(stderr, "unexpected EOF\n");
  800.         exit(2);
  801.     }
  802.     n = strlen(ibuf);
  803.     if (n >= 7 && ibuf[0] == '#' && ibuf[n-6] == '$') {
  804.         if (n >= 11)
  805.             strncpy(mh.m_type, &ibuf[1], 4);
  806.         if (n >= 15)
  807.             strncpy(mh.m_author, &ibuf[5], 4);
  808.         sscanf(&ibuf[n-5], "%4hx", &mh.m_flags);
  809.     }
  810. }
  811.  
  812. do_o_forks()
  813. {
  814.     char ibuf[BUFSIZ];
  815.     int forks = 0, found_crc = 0;
  816.     unsigned int calc_crc, file_crc;
  817.     extern long make_file();
  818.  
  819.  
  820.     crc = 0;    /* calculate a crc for both forks */
  821.  
  822.     /* create empty files ahead of time */
  823.     close(creat(files.f_data, 0666));
  824.     close(creat(files.f_rsrc, 0666));
  825.  
  826.     while (!found_crc && fgets(ibuf, BUFSIZ, ifp) != NULL) {
  827.         if (forks == 0 && strncmp(ibuf, "***COMPRESSED", 13) == 0) {
  828.             compressed++;
  829.             continue;
  830.         }
  831.         if (strncmp(ibuf, "***DATA", 7) == 0) {
  832.             mh.m_datalen = make_file(files.f_data, compressed);
  833.             forks++;
  834.             continue;
  835.         }
  836.         if (strncmp(ibuf, "***RESOURCE", 11) == 0) {
  837.             mh.m_rsrclen = make_file(files.f_rsrc, compressed);
  838.             forks++;
  839.             continue;
  840.         }
  841.         if (compressed && strncmp(ibuf, "***CRC:", 7) == 0) {
  842.             found_crc++;
  843.             calc_crc = crc;
  844.             sscanf(&ibuf[7], "%x", &file_crc);
  845.             break;
  846.         }
  847.         if (!compressed && strncmp(ibuf, "***CHECKSUM:", 12) == 0) {
  848.             found_crc++;
  849.             calc_crc = crc & BYTEMASK;
  850.             sscanf(&ibuf[12], "%x", &file_crc);
  851.             file_crc &= BYTEMASK;
  852.             break;
  853.         }
  854.     }
  855.  
  856.     if (found_crc)
  857.         verify_crc(calc_crc, file_crc);
  858.     else {
  859.         fprintf(stderr, "missing CRC\n");
  860.         exit(3);
  861.     }
  862. }
  863.  
  864. long
  865. make_file(fname, compressed)
  866. char *fname;
  867. int compressed;
  868. {
  869.     char ibuf[BUFSIZ];
  870.     FILE *outf;
  871.     register long nbytes = 0L;
  872.  
  873.     outf = fopen(fname, "w");
  874.     if (outf == NULL) {
  875.         perror(fname);
  876.         exit(-1);
  877.     }
  878.  
  879.     while (fgets(ibuf, BUFSIZ, ifp) != NULL) {
  880.         if (strncmp(ibuf, "***END", 6) == 0)
  881.             break;
  882.         if (compressed)
  883.             nbytes += comp_to_bin(ibuf, outf);
  884.         else
  885.             nbytes += hex_to_bin(ibuf, outf);
  886.     }
  887.  
  888.     fclose(outf);
  889.     return nbytes;
  890. }
  891.  
  892. comp_c_crc(c)
  893. unsigned char c;
  894. {
  895.     crc = (crc + c) & WORDMASK;
  896.     crc = ((crc << 3) & WORDMASK) | (crc >> 13);
  897. }
  898.  
  899. comp_e_crc(c)
  900. unsigned char c;
  901. {
  902.     crc += c;
  903. }
  904.  
  905. #define SIXB(c) (((c)-0x20) & 0x3f)
  906.  
  907. comp_to_bin(ibuf, outf)
  908. char ibuf[];
  909. FILE *outf;
  910. {
  911.     char obuf[BUFSIZ];
  912.     register char *ip = ibuf;
  913.     register char *op = obuf;
  914.     register int n, outcount;
  915.     int numread, incount;
  916.  
  917.     numread = strlen(ibuf);
  918.     ip[numread-1] = ' ';        /* zap out the newline */
  919.     outcount = (SIXB(ip[0]) << 2) | (SIXB(ip[1]) >> 4);
  920.     incount = ((outcount / 3) + 1) * 4;
  921.     for (n = numread; n < incount; n++)    /* restore lost spaces */
  922.         ibuf[n] = ' ';
  923.  
  924.     n = 0;
  925.     while (n <= outcount) {
  926.         *op++ = SIXB(ip[0]) << 2 | SIXB(ip[1]) >> 4;
  927.         *op++ = SIXB(ip[1]) << 4 | SIXB(ip[2]) >> 2;
  928.         *op++ = SIXB(ip[2]) << 6 | SIXB(ip[3]);
  929.         ip += 4;
  930.         n += 3;
  931.     }
  932.  
  933.     for (n=1; n <= outcount; n++)
  934.         comp_c_crc((unsigned)obuf[n]);
  935.  
  936.     fwrite(obuf+1, 1, outcount, outf);
  937.     return outcount;
  938. }
  939.  
  940. hex_to_bin(ibuf, outf)
  941. char ibuf[];
  942. FILE *outf;
  943. {
  944.     register char *ip = ibuf;
  945.     register int n, outcount;
  946.     int c;
  947.  
  948.     n = strlen(ibuf) - 1;
  949.     outcount = n / 2;
  950.     for (n = 0; n < outcount; n++) {
  951.         c = hexit(*ip++);
  952.         comp_e_crc((unsigned)(c = (c << 4) | hexit(*ip++)));
  953.         fputc(c, outf);
  954.     }
  955.     return outcount;
  956. }
  957.  
  958. hexit(c)
  959. int c;
  960. {
  961.     if ('0' <= c && c <= '9')
  962.         return c - '0';
  963.     if ('A' <= c && c <= 'F')
  964.         return c - 'A' + 10;
  965.  
  966.     fprintf(stderr, "illegal hex digit: %c", c);
  967.     exit(4);
  968.     /* NOTREACHED */
  969. }
  970.  
  971. put2(bp, value)
  972. char *bp;
  973. short value;
  974. {
  975.     *bp++ = (value >> 8) & BYTEMASK;
  976.     *bp++ = value & BYTEMASK;
  977. }
  978.  
  979. put4(bp, value)
  980. char *bp;
  981. long value;
  982. {
  983.     register int i, c;
  984.  
  985.     for (i = 0; i < 4; i++) {
  986.         c = (value >> 24) & BYTEMASK;
  987.         value <<= 8;
  988.         *bp++ = c;
  989.     }
  990. }
  991. SHAR_EOF
  992. echo shar: extracting xbin.l
  993. cat - << \SHAR_EOF > xbin.l
  994. .TH XBIN local "24 Mar 1985"
  995. .UC 4
  996. .SH NAME
  997. xbin \- convert mailable format BinHex file into binary before downloading
  998. to MacTerminal
  999. .SH SYNOPSIS
  1000. .B xbin
  1001. [
  1002. .B \-o
  1003. ]
  1004. [
  1005. .B \-v
  1006. ]
  1007. [
  1008. .B \-l
  1009. ]
  1010. [[
  1011. .B \-n
  1012. name
  1013. ] file] ...
  1014. .SH DESCRIPTION
  1015. .I Xbin
  1016. converts a file created by BinHex (usually
  1017. named with one of the extensions ".hex", ".hcx", or ".hqx")
  1018. into three host-system files suitable for downloading to a
  1019. Macintosh via macput.
  1020. This program is designed for use with the 1.1 Release
  1021. version of MacTerminal, but includes a compatibility option for the
  1022. old -0.15X Almost-Alpha version.
  1023. .PP
  1024. The
  1025. .B -l
  1026. (list) option reads the header information and
  1027. prints out all the useful information there,
  1028. without creating any converted output files.
  1029. .PP
  1030. The
  1031. .B -v
  1032. (verbose) option prints a line for each file to be converted, indicating
  1033. the input and output file names.
  1034. .PP
  1035. The
  1036. .B -n
  1037. name
  1038. option allows the user to specify the name to use when creating
  1039. the host files and the eventual name to use on the mac.
  1040. This option must precede the input file name it is to affect.
  1041. .PP
  1042. If this option is not used, the names will be derived from
  1043. either the input file name (.hex or .hcx files),
  1044. or the name encoded in the header information (.hqx files).
  1045. Spaces and slashes will be converted to underscores, and
  1046. the .h?x extension will be deleted, if one is included in the
  1047. input file name.
  1048. .PP
  1049. A file name of "-" indicates that the input should be taken from stdin.
  1050. If no mac file name is specified, the default name (for .hex or .hcx files)
  1051. is "stdin".
  1052. .PP
  1053. Mail or news headers and signatures need not be manually
  1054. stripped -- xbin will ignore pretty much anything
  1055. it doesn't need.
  1056. .PP
  1057. .I xbin
  1058. creates three host-system files from each input file:
  1059. .IB name .info ,
  1060. .IB name .data ,
  1061. and
  1062. .IB name .rsrc .
  1063. .PP
  1064. The
  1065. .B \-o
  1066. flag specifies "old" (version -0.15X) MacTerminal compatibility mode.
  1067. .SH BUGS
  1068. The "LOCKED" bit in the flags cannot be set by xbin.
  1069. This is due to a bug in MacTerminal, which sets the flags
  1070. when the file is created, rather than after it has been
  1071. transfered, resulting in it not being able to write the
  1072. file.
  1073. .PP
  1074. Input files must contain a line starting with "(This file"
  1075. to detect the beginning of the BinHex information.
  1076. .SH SEE ALSO
  1077. macput(1), macget(1)
  1078. .SH AUTHOR
  1079. Dave Johnson, Brown 12/16/84;
  1080. CRC handling code by Darin Adler, TMQ Software 3/16/85
  1081. SHAR_EOF
  1082.