home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / mac / 3 < prev    next >
Encoding:
Internet Message Format  |  1991-02-21  |  48.7 KB

  1. From: np@doc.imperial.ac.uk (Nigel Perry)
  2. Newsgroups: comp.sources.mac
  3. Subject: AufsTools (part 3 of 4)
  4. Message-ID: <CSM.91.3>
  5. Date: 20 Feb 91 20:00:06 GMT
  6. Approved: bytebug@dhw68k.cts.com (Roger L. Long)
  7.  
  8. [AufsTools - part 3 of 4]
  9.  
  10. ---
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 3 (of 4)."
  18. # Contents:  mcvert/hqxify.c mcvert/mcvert.c stuffit/sit.c
  19. # Wrapped by np@asun5 on Mon Dec  3 13:15:58 1990
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'mcvert/hqxify.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'mcvert/hqxify.c'\"
  23. else
  24. echo shar: Extracting \"'mcvert/hqxify.c'\" \(19992 characters\)
  25. sed "s/^X//" >'mcvert/hqxify.c' <<'END_OF_FILE'
  26. X#include "mactypes.h"
  27. X
  28. X#define HQXBUFLEN 512
  29. Xbyte hqxbuf[HQXBUFLEN+1], *buf_ptr, *buf_end, *buf_start=hqxbuf+1;
  30. X
  31. X#define MAXLINE 255
  32. Xbyte line[MAXLINE+1], *line_ptr, *line_end, *line_start=line+1;
  33. X
  34. Xint line_count, file_count;
  35. Xint save_state, total_bytes, save_run_length;
  36. Xword save_nibble;
  37. Xchar binfname[BINNAMELEN], hqxfname[BINNAMELEN];
  38. XFILE *hqxfile, *binfile;
  39. X
  40. X/* This routine reads the header of a hqxed file and appropriately twiddles it,
  41. X    determines if it has CRC problems, creates the .bin file, and puts the info
  42. X    into the .bin file.
  43. X    Output is hqx_datalen, hqx_rsrclen, type, binfname, binfile */
  44. X
  45. Xhqx_to_bin_hdr(type, hqx_datalen, hqx_rsrclen)
  46. Xchar *type;
  47. Xulong *hqx_datalen, *hqx_rsrclen;
  48. X{   register byte *hqx_ptr, *hqx_end;
  49. X    register ulong calc_crc;
  50. X    hqx_buf *hqx_block;
  51. X    hqx_header *hqx;
  52. X    info_header info;
  53. X    ulong mtim;
  54. X    short crc;
  55. X
  56. X    extern word magic[];
  57. X    extern FILE *verbose;
  58. X    extern char *dir, *ext;
  59. X    extern short calc_mb_crc();
  60. X
  61. X    /* read the hqx header, assuming that I won't exhaust hqxbuf in so doing */
  62. X    fill_hqxbuf();
  63. X    hqx_block = (hqx_buf *) buf_ptr;
  64. X    hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen);
  65. X    hqx_ptr = buf_ptr;
  66. X    hqx_end = (byte *) hqx + sizeof(hqx_header) - 1;
  67. X    calc_crc = 0;
  68. X    while (hqx_ptr < hqx_end)
  69. X        calc_crc = (((calc_crc&0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8];
  70. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  71. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  72. X    buf_ptr = hqx_ptr;
  73. X
  74. X    /* stuff the hqx header data into the info header */
  75. X    bzero(&info, sizeof(info_header));
  76. X    info.nlen = hqx_block->nlen;
  77. X    strncpy(info.name, hqx_block->name, info.nlen);     /* name */
  78. X    bcopy(hqx->type, info.type, 9);             /* type, author, flag */
  79. X    info.flags  &= 0x7e;                        /* reset lock bit, init bit */
  80. X    if (hqx->protect & 0x40) info.protect = 1;  /* copy protect bit */
  81. X    bcopy(hqx->dlen, info.dlen, 8);             /* dlen, rlen */
  82. X    mtim = time2mac(time(0));
  83. X    bcopy(&mtim, info.mtim, 4);
  84. X    bcopy(&mtim, info.ctim, 4);
  85. X    info.uploadvers = '\201';
  86. X    info.readvers = '\201';
  87. X
  88. X    /* calculate MacBinary CRC */
  89. X    crc = calc_mb_crc(&info, 124, 0);
  90. X    info.crc[0] = (char) (crc >> 8);
  91. X    info.crc[1] = (char) crc;
  92. X
  93. X    /* Create the .bin file and write the info to it */
  94. X    unixify(hqx_block->name);
  95. X    sprintf(binfname, "%s/%s%s", dir, hqx_block->name, ext);
  96. X    fprintf(verbose,
  97. X        "Converting     %-30s type = \"%4.4s\", author = \"%4.4s\"\n",
  98. X        hqx_block->name, info.type, info.auth);
  99. X    if ((binfile = fopen(binfname, "w")) == NULL)
  100. X        error("Cannot open %s", binfname);
  101. X    check_hqx_crc(calc_crc, "File header CRC mismatch in %s", binfname);
  102. X    fwrite(&info, sizeof(info), 1, binfile);
  103. X
  104. X    /* Get a couple of items we'll need later */
  105. X    bcopy(info.dlen, hqx_datalen, 4);
  106. X    *hqx_datalen = mac2long(*hqx_datalen);
  107. X    bcopy(info.rlen, hqx_rsrclen, 4);
  108. X    *hqx_rsrclen = mac2long(*hqx_rsrclen);
  109. X    bcopy(info.type, type, 4);
  110. X    }
  111. X
  112. X/* This routine reads the header of a bin file and appropriately twiddles it,
  113. X    creates the .hqx file, and puts the info into the .hqx file.
  114. X    Output is hqx_datalen, hqx_rsrclen, type, hqxfname, hqxfile */
  115. X
  116. Xbin_to_hqx_hdr(hqx_datalen, hqx_rsrclen)
  117. Xulong *hqx_datalen, *hqx_rsrclen;
  118. X{   register byte *hqx_ptr, *hqx_end;
  119. X    register ulong calc_crc;
  120. X    hqx_buf *hqx_block;
  121. X    hqx_header *hqx;
  122. X    info_header info;
  123. X    extern word magic[];
  124. X    extern FILE *verbose;
  125. X    extern char **hqxnames_left;
  126. X    extern char *ext;
  127. X
  128. X    strcpy(binfname, *hqxnames_left++);
  129. X    if (!(binfile = fopen(binfname, "r"))) {
  130. X        /* Maybe we are supposed to figure out the suffix ourselves? */
  131. X        strcat(binfname, ext);
  132. X        if (!(binfile = fopen(binfname, "r")))
  133. X            error("Cannot open %s", binfname);
  134. X        }
  135. X    if (!fread(&info, sizeof(info), 1, binfile))
  136. X        error("Unexpected EOF in header of %s", binfname);
  137. X
  138. X    /* stuff the info header into the hqx header */
  139. X    hqx_block = (hqx_buf *) buf_ptr;
  140. X    hqx_block->nlen = info.nlen;
  141. X    strncpy(hqx_block->name, info.name, info.nlen);
  142. X    hqx = (hqx_header *) (hqx_block->name + hqx_block->nlen);
  143. X    hqx->version  = 0;
  144. X    bcopy(info.type, hqx->type, 9);             /* type, author, flags */
  145. X    if (info.protect = 1) hqx->protect = 0;     /* protect bit: 0x40 */
  146. X    else hqx->protect = 0;
  147. X    bcopy(info.dlen, hqx->dlen, 8);             /* dlen, rlen */
  148. X
  149. X    /* Create the .hqx file and write the info to it */
  150. X    strncpy(hqxfname, info.name, info.nlen);
  151. X    hqxfname[info.nlen] = '\0';
  152. X    unixify(hqxfname);
  153. X    fprintf(verbose,
  154. X        "Converting     %-30s type = \"%4.4s\", author = \"%4.4s\"\n",
  155. X        hqxfname, info.type, info.auth);
  156. X
  157. X    calc_crc = 0;
  158. X    hqx_ptr = (byte *) hqx_block;
  159. X    hqx_end = hqx_ptr + hqx_block->nlen + sizeof(hqx_header);
  160. X    while (hqx_ptr < hqx_end)
  161. X        calc_crc = (((calc_crc&0xff) << 8) | *hqx_ptr++) ^ magic[calc_crc >> 8];
  162. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  163. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  164. X    buf_ptr = hqx_end;
  165. X    write_hqx_crc(calc_crc);
  166. X
  167. X    /* Get a couple of items we'll need later */
  168. X    bcopy(info.dlen, hqx_datalen, 4);
  169. X    *hqx_datalen = mac2long(*hqx_datalen);
  170. X    bcopy(info.rlen, hqx_rsrclen, 4);
  171. X    *hqx_rsrclen = mac2long(*hqx_rsrclen);
  172. X    }
  173. X
  174. X
  175. X/* This routine copies bytes from the decoded input stream to the output.  
  176. X    It also pads to a multiple of 128 bytes on the output, which is part
  177. X    of the .bin format */
  178. Xword hqx_to_bin_fork(nbytes)
  179. Xregister ulong nbytes;
  180. X{   register byte *c;
  181. X    register ulong calc_crc;
  182. X    register int c_length;
  183. X    ulong extra_bytes;
  184. X    extern word magic[];
  185. X
  186. X    extra_bytes = 127 - (nbytes+127)%128; /* pad fork to mult of 128 bytes */
  187. X    calc_crc = 0;
  188. X    for (;;) {
  189. X        c = buf_ptr;
  190. X        c_length = (c + nbytes > buf_end) ? buf_end - c : nbytes;
  191. X        nbytes -= c_length;
  192. X        fwrite(c, sizeof(byte), c_length, binfile);
  193. X        while (c_length--)
  194. X            calc_crc = (((calc_crc&0xff) << 8) | *c++) ^ magic[calc_crc >> 8];
  195. X        if (!nbytes) break;
  196. X        fill_hqxbuf();
  197. X        }
  198. X    buf_ptr = c;
  199. X    while (extra_bytes--) putc(0, binfile);
  200. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  201. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  202. X    return (word) calc_crc;
  203. X    }
  204. X
  205. X/* This routine copies bytes from the input stream to the encoded output.  
  206. X    It also pads to a multiple of 128 bytes on the input, which is part
  207. X    of the .bin format */
  208. Xword bin_to_hqx_fork(nbytes)
  209. Xregister ulong nbytes;
  210. X{   register byte *c;
  211. X    register ulong calc_crc;
  212. X    register int c_length;
  213. X    ulong extra_bytes;
  214. X    extern word magic[];
  215. X
  216. X    extra_bytes = 127 - (nbytes+127)%128; /* pad fork to mult of 128 bytes */
  217. X    calc_crc = 0;
  218. X    for (;;) {
  219. X        c = buf_ptr;
  220. X        c_length = (c + nbytes > buf_end) ? buf_end - c : nbytes;
  221. X        nbytes -= c_length;
  222. X        fread(c, sizeof(byte), c_length, binfile);
  223. X        buf_ptr += c_length;
  224. X        while (c_length--)
  225. X            calc_crc = (((calc_crc&0xff) << 8) | *c++) ^ magic[calc_crc >> 8];
  226. X        if (!nbytes) break;
  227. X        empty_hqxbuf();
  228. X        }
  229. X    buf_ptr = c;
  230. X
  231. X    fseek(binfile, extra_bytes, 1);
  232. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  233. X    calc_crc = ((calc_crc&0xff) << 8) ^ magic[calc_crc >> 8];
  234. X    return (word) calc_crc;
  235. X    }
  236. X
  237. X/* Essentials for Binhex 8to6 run length encoding */
  238. X#define RUNCHAR 0x90
  239. X#define MAXRUN 255
  240. X#define IS_LEGAL <0x40
  241. X#define ISNT_LEGAL >0x3f
  242. X#define DONE 0x7F /* tr68[':'] = DONE, since Binhex terminator is ':' */
  243. X#define SKIP 0x7E /* tr68['\n'|'\r'] = SKIP, i. e. end of line char.  */
  244. X#define FAIL 0x7D /* character illegal in binhex file */
  245. X
  246. Xbyte tr86[] =
  247. X        "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"; 
  248. Xbyte tr68[] = {
  249. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  250. X    FAIL, FAIL, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL,
  251. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  252. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  253. X    FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
  254. X    0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, FAIL, FAIL,
  255. X    0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, FAIL,
  256. X    0x14, 0x15, DONE, FAIL, FAIL, FAIL, FAIL, FAIL,
  257. X    0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
  258. X    0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, FAIL,
  259. X    0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, FAIL,
  260. X    0x2C, 0x2D, 0x2E, 0x2F, FAIL, FAIL, FAIL, FAIL,
  261. X    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, FAIL,
  262. X    0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, FAIL, FAIL,
  263. X    0x3D, 0x3E, 0x3F, FAIL, FAIL, FAIL, FAIL, FAIL,
  264. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  265. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  266. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  267. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  268. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  269. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  270. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  271. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  272. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  273. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  274. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  275. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  276. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  277. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  278. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  279. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  280. X    FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
  281. X    };
  282. X
  283. X/*
  284. X *  This procedure transparently reads and decodes the hqx input.  It does run 
  285. X *  length and 6 to 8 decoding.
  286. X */
  287. X#define READING 0
  288. X#define SKIPPING 1
  289. X#define FIND_START_COLON 2
  290. X
  291. X/* NP 12/3/90: A dirty hack to handle "X-Mailer: ELM [version 2.2 PL14]" */
  292. X#define X_MAIL_STR "\054\014\043\061\070\073\065\077\177"
  293. X#define X_MAIL_LEN strlen(X_MAIL_STR)
  294. X
  295. Xfill_hqxbuf()
  296. X{   register ulong c, nibble;
  297. X    register int not_in_a_run = TRUE, state68;
  298. X    register byte *fast_buf, *fast_line;
  299. X    static int status = FIND_START_COLON;
  300. X
  301. X    buf_ptr = fast_buf = buf_start;
  302. X    fast_line = line_ptr;
  303. X    state68 = save_state;
  304. X    nibble = save_nibble;
  305. X    if (save_run_length > 0) {
  306. X        c = save_run_length;
  307. X        save_run_length = 0;
  308. X        goto continue_run;
  309. X        }
  310. X    while (fast_buf < buf_end) {
  311. X        next_char:
  312. X        if ((c = *fast_line++) ISNT_LEGAL) {
  313. X            if (c == DONE) break;
  314. X            next_line:
  315. X            if (!fgets(line_start, MAXLINE, hqxfile) && !new_in_hqx_file())
  316. X                if (status == FIND_START_COLON) exit(0);
  317. X        else error("Premature EOF in %s\n", hqxfname);
  318. X            line_ptr = line_start;
  319. X            scan_line:
  320. X            fast_line = line_ptr;
  321. X        while ((*fast_line = tr68[*fast_line]) IS_LEGAL) fast_line++;
  322. X        c = *fast_line;
  323. X            switch (status) {
  324. X            case READING:
  325. X                if (c == SKIP && fast_line == line_end) break;
  326. X                if (c == DONE) {
  327. X                    status = FIND_START_COLON;
  328. X                    break;
  329. X                    }
  330. X                status = SKIPPING;
  331. X                goto next_line;
  332. X            case SKIPPING:
  333. X                if (c == SKIP && fast_line == line_end) {
  334. X                    status = READING;
  335. X                    break;
  336. X                    }
  337. X        /*  GMT, 1/9/90: Added this clause to avoid losing the last
  338. X         *  line if it was preceeded by a skipped line.  */
  339. X                if (c == DONE) { 
  340. X          /* NP 12/3/90: A dirty hack to handle "X-Mailer: ELM [version 2.2 PL14]" */
  341. X          if( (fast_line - line_ptr == X_MAIL_LEN - 1)
  342. X             && (strncmp(line_ptr, X_MAIL_STR, X_MAIL_LEN) == 0)) goto next_line;
  343. X                    status = FIND_START_COLON;
  344. X                    break;
  345. X                    }
  346. X                goto next_line;
  347. X            case FIND_START_COLON:
  348. X                if (*line_start == DONE) {
  349. X                    status = READING;
  350. X                    line_ptr++;
  351. X                    goto scan_line;
  352. X                    }
  353. X                goto next_line;
  354. X                }
  355. X            fast_line = line_ptr;
  356. X            c = *fast_line++;
  357. X      }
  358. X
  359. X        /* Finally, we have the next 6 bits worth of data */
  360. X        switch (state68++) {
  361. X        case 0:
  362. X            nibble = c;
  363. X            goto next_char;
  364. X        case 1:
  365. X            nibble = (nibble << 6) | c;
  366. X            c = nibble >> 4;
  367. X            break;
  368. X        case 2:
  369. X            nibble = (nibble << 6) | c;
  370. X            c = (nibble >> 2) & 0xff;
  371. X            break;
  372. X        case 3:
  373. X            c = (nibble << 6) & 0xff | c;
  374. X            state68 = 0;
  375. X            break;
  376. X            }
  377. X        if (not_in_a_run)
  378. X            if (c != RUNCHAR) *fast_buf++ = c;
  379. X            else {not_in_a_run = FALSE; goto next_char;}
  380. X        else {
  381. X            if (c--) {
  382. X                not_in_a_run = buf_end - fast_buf;
  383. X                if (c > not_in_a_run) {
  384. X                    save_run_length = c - not_in_a_run;
  385. X                    c = not_in_a_run;
  386. X                    }
  387. X                continue_run:
  388. X                not_in_a_run = fast_buf[-1];
  389. X                while (c--) *fast_buf++ = not_in_a_run;
  390. X                }
  391. X            else *fast_buf++ = RUNCHAR;
  392. X            not_in_a_run = TRUE;
  393. X            }
  394. X        }
  395. X    total_bytes += fast_buf - buf_ptr;
  396. X    buf_start[-1] = fast_buf[-1];
  397. X    line_ptr = fast_line;
  398. X    save_state = state68;
  399. X    save_nibble = nibble;
  400. X    }
  401. X
  402. X
  403. Xnew_in_hqx_file()
  404. X{   char *hqx_ext;
  405. X    extern char **hqxnames_left;
  406. X    if (*hqxnames_left[0] == '\0' || *hqxnames_left[0] == '-') return FALSE;
  407. X    strcpy(hqxfname, *hqxnames_left++);
  408. X    hqx_ext = hqxfname + strlen(hqxfname) - 4;
  409. X    if (!strcmp(hqx_ext, ".hqx"))
  410. X        if (!freopen(hqxfname, "r", hqxfile))
  411. X            error("Cannot open %s\n", hqxfname);
  412. X            else;
  413. X    else {
  414. X        if (!freopen(hqxfname, "r", hqxfile)) {
  415. X            hqx_ext += 4;
  416. X            strcpy(hqx_ext, ".hqx");
  417. X            if (!freopen(hqxfname, "r", hqxfile)) {
  418. X                error("Cannot find %s\n", hqxfname);
  419. X            }
  420. X        }
  421. X      }
  422. X    fgets(line_start, MAXLINE, hqxfile);
  423. X    return TRUE;
  424. X    }
  425. X
  426. X/*
  427. X *  This procedure transparently encodes and writes the hqx output.  
  428. X *  It does run length and 8 to 6 encoding.
  429. X */
  430. Xempty_hqxbuf()
  431. X{   register ulong c, nibble, last_c;
  432. X    register byte *fast_buf, *fast_line;
  433. X    register int state86, dont_look_for_runs = FALSE, run_length;
  434. X    extern int maxlines;
  435. X
  436. X    run_length = save_run_length;
  437. X    last_c = buf_start[-1];
  438. X    fast_buf = buf_start;
  439. X    fast_line = line_ptr;
  440. X    state86 = save_state;
  441. X    nibble = save_nibble;
  442. X    while (fast_buf < buf_ptr) {
  443. X        c = *fast_buf++;
  444. X        if (dont_look_for_runs) dont_look_for_runs = FALSE;
  445. X        else if (last_c == c &&  run_length < MAXRUN) {run_length++; continue;}
  446. X        else {
  447. X            if (run_length >1) {
  448. X                --fast_buf;
  449. X                if (run_length == 2 && last_c != RUNCHAR) c = last_c;
  450. X                else {
  451. X                    c = RUNCHAR;
  452. X                    *--fast_buf = run_length;
  453. X                    dont_look_for_runs = TRUE;
  454. X                    }
  455. X                run_length = 1;
  456. X                }
  457. X            else last_c = c;
  458. X            if (c == RUNCHAR && !dont_look_for_runs) {
  459. X                *--fast_buf = 0;
  460. X                dont_look_for_runs = TRUE;
  461. X                }
  462. X            }
  463. X
  464. X        if (fast_line == line_end) {
  465. X            if (line_count++ == maxlines) new_out_hqx_file();
  466. X            fputs(line_start, hqxfile);
  467. X            fast_line = line_start;
  468. X            }
  469. X
  470. X        switch (state86++) {
  471. X        case 0:
  472. X            *fast_line++ = tr86[ c >> 2 ];
  473. X            nibble = (c << 4) & 0x3f;
  474. X            break;
  475. X        case 1:
  476. X            *fast_line++ = tr86[ (c >> 4) | nibble ];
  477. X            nibble = (c << 2) & 0x3f;
  478. X            break;
  479. X        case 2:
  480. X            *fast_line++ = tr86[ (c >> 6) | nibble ];
  481. X            if (fast_line == line_end) {
  482. X                if (line_count++ == maxlines) new_out_hqx_file();
  483. X                fputs(line_start, hqxfile);
  484. X                fast_line = line_start;
  485. X                }
  486. X            *fast_line++ = tr86[ c & 0x3f ];
  487. X            state86 = 0;
  488. X            break;
  489. X            }
  490. X        }
  491. X    save_run_length = run_length;
  492. X    buf_start[-1] = last_c;
  493. X    buf_ptr = buf_start;
  494. X    line_ptr = fast_line;
  495. X    save_state = state86;
  496. X    save_nibble = nibble;
  497. X    }
  498. X
  499. Xnew_out_hqx_file()
  500. X{   char filename[NAMELEN + 7];
  501. X    extern int maxlines;
  502. X    fprintf(hqxfile, "<<< End of Part %2d >>>\n", file_count);
  503. X    fclose(hqxfile);
  504. X    file_count++;
  505. X    if (maxlines) sprintf(filename, "%s%02d.hqx", hqxfname, file_count);
  506. X    else sprintf(filename, "%s.hqx", hqxfname);
  507. X    if ((hqxfile = fopen(filename, "w")) == NULL)
  508. X        error("Can't create %s", filename);
  509. X    if (file_count > 1)
  510. X        fprintf(hqxfile, "<<< Start of Part %2d >>>\n", file_count);
  511. X    else fprintf(hqxfile, "(This file must be converted with BinHex 4.0)\n\n");
  512. X    line_count = 3;
  513. X    }
  514. X
  515. Xcheck_hqx_crc(calc_crc, msg, name)
  516. Xword calc_crc;
  517. Xchar msg[], name[];
  518. X{   word read_crc;
  519. X    if (buf_ptr >= buf_end) fill_hqxbuf();
  520. X    read_crc = *buf_ptr++ << 8;
  521. X    if (buf_ptr >= buf_end) fill_hqxbuf();
  522. X    read_crc |= *buf_ptr++;
  523. X    if (read_crc != calc_crc) error(msg, name);
  524. X    }
  525. X
  526. Xwrite_hqx_crc(calc_crc)
  527. Xword calc_crc;
  528. X{   if (buf_ptr == buf_end) empty_hqxbuf();
  529. X    *buf_ptr++ = calc_crc >> 8;
  530. X    if (buf_ptr == buf_end) empty_hqxbuf();
  531. X    *buf_ptr++ = calc_crc;
  532. X    }
  533. X
  534. Xun_hqx(unpit_flag)
  535. Xint unpit_flag;
  536. X{   char type[4];
  537. X    ulong hqx_datalen, hqx_rsrclen;
  538. X    word un_pit();
  539. X    int unpitting, bytes_read;
  540. X    word calc_crc;
  541. X    extern char **hqxnames_left;
  542. X
  543. X    hqxfile = fopen("/dev/null", "r");
  544. X    line_end = line_start + HQXLINELEN;
  545. X    buf_end = buf_start + HQXBUFLEN;
  546. X    for (;;) {
  547. X        total_bytes = 0;
  548. X        line_ptr = line_start;
  549. X        line_ptr[0] = SKIP;
  550. X        save_state = 0;
  551. X        save_run_length = 0;
  552. X
  553. X        hqx_to_bin_hdr(type, &hqx_datalen, &hqx_rsrclen); /* binfname */
  554. X
  555. X        unpitting = unpit_flag && !strcmp(type, "PIT ");
  556. X        if (unpitting) {
  557. X            fclose(binfile);
  558. X            unlink(binfname);
  559. X            bytes_read = total_bytes - (buf_end - buf_ptr);
  560. X            calc_crc = un_pit();
  561. X            bytes_read = total_bytes - (buf_end - buf_ptr) - bytes_read;
  562. X            if (bytes_read != hqx_datalen)
  563. X                fprintf(stderr,
  564. X                  "Warning - Extraneous characters ignored in %s\n", binfname);
  565. X            }
  566. X        else calc_crc = hqx_to_bin_fork(hqx_datalen);
  567. X        check_hqx_crc(calc_crc, "File data CRC mismatch in %s", binfname);
  568. X
  569. X        calc_crc = hqx_to_bin_fork(hqx_rsrclen);
  570. X        check_hqx_crc(calc_crc, "File rsrc CRC mismatch in %s", binfname);
  571. X
  572. X        if (!unpitting) fclose(binfile);
  573. X        }
  574. X    }
  575. X
  576. Xre_hqx()
  577. X{   word calc_crc;
  578. X    ulong hqx_datalen, hqx_rsrclen;
  579. X    extern char **hqxnames_left;
  580. X    extern int maxlines;
  581. X    line_end = line_start + HQXLINELEN;
  582. X    buf_end = buf_start + HQXBUFLEN;
  583. X    while (*hqxnames_left[0] != '-') {
  584. X        hqxfile = fopen("/dev/null", "w");
  585. X        line_count = maxlines;
  586. X        file_count = 0;
  587. X        line_ptr = line_start;
  588. X        *line_ptr++ = ':';
  589. X        strcpy(line_end, "\n");
  590. X        buf_ptr = buf_start;
  591. X        save_state = 0;
  592. X        save_run_length = 1;
  593. X
  594. X        bin_to_hqx_hdr(&hqx_datalen, &hqx_rsrclen);   /* calculates hqxfname */
  595. X
  596. X        calc_crc = bin_to_hqx_fork(hqx_datalen);
  597. X        write_hqx_crc(calc_crc);
  598. X
  599. X        calc_crc = bin_to_hqx_fork(hqx_rsrclen);
  600. X        write_hqx_crc(calc_crc);
  601. X        *buf_ptr = !buf_ptr[-1];      /* To end a run and to get the last */
  602. X        buf_ptr++;
  603. X        empty_hqxbuf();                 /* stray bits, temporarily add a char */
  604. X        if (save_state != 2) --line_ptr;
  605. X        if (line_ptr == line_end) {
  606. X            fputs(line_start, hqxfile);
  607. X            line_ptr = line_start;
  608. X            }
  609. X        strcpy(line_ptr, ":\n");
  610. X        fputs(line_start, hqxfile);
  611. X        fclose(hqxfile);
  612. X        }
  613. X    }
  614. END_OF_FILE
  615. if test 19992 -ne `wc -c <'mcvert/hqxify.c'`; then
  616.     echo shar: \"'mcvert/hqxify.c'\" unpacked with wrong size!
  617. fi
  618. # end of 'mcvert/hqxify.c'
  619. fi
  620. if test -f 'mcvert/mcvert.c' -a "${1}" != "-c" ; then 
  621.   echo shar: Will not clobber existing file \"'mcvert/mcvert.c'\"
  622. else
  623. echo shar: Extracting \"'mcvert/mcvert.c'\" \(13666 characters\)
  624. sed "s/^X//" >'mcvert/mcvert.c' <<'END_OF_FILE'
  625. X/* mcvert.c - version 1.05 - 10 January, 1990 modified 12 March, 1990 by NP
  626. X * Written by Doug Moore - Rice University - dougm@rice.edu - April '87
  627. X * Sun bug fixes, assorted stuff - Jim Sasaki, March '89
  628. X * Changed default max_line_size from 2000 to unlimited - Doug Moore, April, '89
  629. X * Sun 3/60 doesn't like odd-sized structs.  Bug fixed - Doug Moore, April, '89
  630. X *                                              - aided by Spencer W. Thomas
  631. X * Didn't handle properly many hqx files combined in one file.  Bug fixed -
  632. X *                                           Doug Moore, June, '89
  633. X * Modified to handle MacBinaryII specification. Jim Van Verth, Sept, '89
  634. X *
  635. X * Fixed a bug when there are blank lines in hqx data, as happens when newline
  636. X * get translated to CRLF and then to \n\n, common for some file transfers.
  637. X * The last hqx line would be lost if the previous line was blank or junk.
  638. X *    Glenn Trewitt, Stanford University, 1990    (1.05)
  639. X *
  640. X * Mcvert would hiccup on mail header lines "X-Mailer: ELM [version 2.2 PL14]"
  641. X * as "X-Mailer:" is a vaild hqx line! Added in code to special case this
  642. X * line and keep scanning for the real hqx data.
  643. X *      Nigel Perry, Imperial College, 12 March 1990 [NP]
  644. X *
  645. X * This program may be freely distributed for non-profit purposes.  It may not
  646. X * be sold, by itself or as part of a collection of software.  It may be freely
  647. X * modified as long as no modified version is distributed.  Modifications of
  648. X * interest to all can be incorporated into the program by sending them to me
  649. X * for distribution.  Parts of the code can be used in other programs.  I am not
  650. X * responsible for any damage caused by this program.  I hope you enjoy it.
  651. X */
  652. X
  653. X#include "mactypes.h"
  654. X
  655. X#define HQX 0
  656. X#define TEXT 1
  657. X#define DATA 2
  658. X#define RSRC 3
  659. X#define HOST 4
  660. X#define FORWARDS 0
  661. X#define BACKWARDS 1
  662. X
  663. XFILE *verbose;
  664. Xchar **hqxnames, **hqxnames_left;
  665. Xchar *dir, *ext, *text_author;
  666. Xchar *maxlines_str;
  667. Xint maxlines;
  668. X
  669. Xmain(argc, argv)
  670. Xint argc;
  671. Xchar **argv;
  672. X{   char *flags, *getenv();
  673. X    int direction, mode, unpit_flag;
  674. X
  675. X    argv++;
  676. X    argc--;
  677. X    verbose = stderr;
  678. X    direction = FORWARDS;
  679. X    mode = HQX;
  680. X    unpit_flag = 0;
  681. X
  682. X    if ((text_author = getenv("MAC_EDITOR"))    == NULL)    text_author = "MACA";
  683. X    if ((ext =         getenv("MAC_EXT"))       == NULL)    ext = ".bin";
  684. X    if ((dir =         getenv("MAC_DLOAD_DIR")) == NULL)    dir = ".";
  685. X    if ((maxlines_str = getenv("MAC_LINE_LIMIT")) == NULL)  maxlines = 0;
  686. X    else                                         maxlines = atoi(maxlines_str);
  687. X    
  688. X    /* Make command line arguments globally accessible */
  689. X    hqxnames = (char **) calloc(argc+1, sizeof(char *));
  690. X    hqxnames_left = hqxnames;
  691. X    while (argc--)  *hqxnames_left++ = *argv++;
  692. X    *hqxnames_left = "-";
  693. X    hqxnames_left = hqxnames;
  694. X
  695. X    while (strcmp(*hqxnames_left, "-")) {
  696. X        if (hqxnames_left[0][0] == '-') {
  697. X            flags = *hqxnames_left++;
  698. X            while (*++flags)
  699. X                switch (*flags) {
  700. X                case 'x':
  701. X                    mode = HQX;
  702. X                    break;
  703. X                case 'u':
  704. X                    mode = TEXT;
  705. X                    break;
  706. X                case 'd':
  707. X                    mode = DATA;
  708. X                    break;
  709. X                case 'r':
  710. X                    mode = RSRC;
  711. X                    break;
  712. X        case 'h':
  713. X            mode = HOST;
  714. X            break;
  715. X                case 'D':
  716. X                    direction = FORWARDS;
  717. X                    break;
  718. X                case 'U':
  719. X                    direction = BACKWARDS;
  720. X                    break;
  721. X                case 'q':
  722. X                    unpit_flag = 0;
  723. X                    break;
  724. X                case 'p':
  725. X                    unpit_flag = 1;
  726. X                    break;
  727. X                case 's':
  728. X                    verbose = fopen("/dev/null", "w");
  729. X                    break;
  730. X                case 'v':
  731. X                    verbose = stderr;
  732. X                    break;
  733. X                default:
  734. X                    error(
  735. X                    "Usage: mcvert [ -[r|d|u|x|h] [D|U] [p|q] [s|v] ] filename...",
  736. X                    NULL);
  737. X                    }
  738. X            }
  739. X
  740. X        if (direction == BACKWARDS)
  741. X            if (mode == HQX && unpit_flag) re_hqx();/* no re_pit() yet */
  742. X            else if (mode == HQX) re_hqx();
  743. X            else re_other(mode);
  744. X        else
  745. X            if (mode == HQX) un_hqx(unpit_flag);
  746. X            else un_other(mode);
  747. X        }
  748. X    }
  749. X
  750. X/* An array useful for CRC calculations that use 0x1021 as the "seed" */
  751. Xword magic[] = {
  752. X    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
  753. X    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
  754. X    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
  755. X    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
  756. X    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
  757. X    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
  758. X    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
  759. X    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
  760. X    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
  761. X    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
  762. X    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
  763. X    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
  764. X    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
  765. X    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
  766. X    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
  767. X    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
  768. X    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
  769. X    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
  770. X    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
  771. X    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
  772. X    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
  773. X    0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
  774. X    0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
  775. X    0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
  776. X    0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
  777. X    0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
  778. X    0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
  779. X    0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
  780. X    0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
  781. X    0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
  782. X    0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
  783. X    0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
  784. X    };
  785. X
  786. X
  787. X/*
  788. X * calc_crc() --
  789. X *   Compute the MacBinary II-style CRC for the data pointed to by p, with the
  790. X *   crc seeded to seed.
  791. X *
  792. X *   Modified by Jim Van Verth to use the magic array for efficiency.
  793. X */
  794. Xshort calc_mb_crc(p, len, seed)
  795. Xunsigned char *p;
  796. Xlong len;
  797. Xshort seed;
  798. X{
  799. X  short hold;      /* crc computed so far */
  800. X  long  i;         /* index into data */
  801. X
  802. X  extern unsigned short magic[];   /* the magic array */
  803. X
  804. X  hold = seed;     /* start with seed */
  805. X  for (i = 0; i < len; i++, p++) {
  806. X    hold ^= (*p << 8);
  807. X    hold = (hold << 8) ^ magic[(unsigned char)(hold >> 8)];
  808. X  }
  809. X
  810. X  return (hold);
  811. X} /* calc_crc() */
  812. X
  813. X
  814. X/* Report a fatal error */
  815. Xerror(msg, name)
  816. Xchar msg[], name[];
  817. X{   fprintf(stderr, msg, name);
  818. X    putc('\n', stderr);
  819. X    exit(1);
  820. X    }
  821. X
  822. X/* replace illegal Unix characters in file name */
  823. X/* make sure host file name doesn't get truncated beyond recognition */
  824. Xunixify(np)
  825. Xregister byte *np;
  826. X{   register ulong c;
  827. X    c = strlen(np);
  828. X    if (c > SYSNAMELEN - 4) c = SYSNAMELEN - 4;
  829. X    np[c] = '\0';
  830. X    np--;
  831. X    while (c = *++np)
  832. X        if (c <= ' ' || c == '/' || c > '~') *np = '_';
  833. X    }
  834. X
  835. X/* Convert Unix time (GMT since 1-1-1970) to Mac
  836. X                                    time (local since 1-1-1904) */
  837. X#define MACTIMEDIFF 0x7c25b080 /* Mac time of 00:00:00 GMT, Jan 1, 1970 */
  838. X
  839. Xulong time2mac(time)
  840. Xulong time;
  841. X{   struct timeb tp;
  842. X    ftime(&tp);
  843. X    return long2mac(time + MACTIMEDIFF
  844. X                    - 60 * (tp.timezone - 60 * tp.dstflag));
  845. X    }
  846. X
  847. X
  848. X/* This procedure copies the input file to the output file, basically, although
  849. X    in TEXT mode it changes LF's to CR's and in any mode it forges a Mac info 
  850. X    header.  Author type for TEXT mode can come from the MAC_EDITOR environ-
  851. X    ment variable if it is defined. */
  852. X
  853. Xun_other(mode)
  854. Xint mode;
  855. X{   register ulong b;
  856. X    register ulong nchars;
  857. X    char txtfname[BINNAMELEN], binfname[BINNAMELEN];
  858. X    FILE *txtfile, *binfile; 
  859. X    char *suffix;
  860. X    struct stat stbuf;
  861. X    info_header info;
  862. X    int extra_chars;
  863. X    ulong dlen, rlen, mtim, ctim;
  864. X    short crc, calc_mb_crc();
  865. X
  866. X    if (mode == DATA) suffix = ".data";
  867. X    else if (mode == RSRC) suffix = ".rsrc";
  868. X    else suffix = ".text";
  869. X
  870. X    while (hqxnames_left[0][0] != '-') {
  871. X
  872. X        strcpy(txtfname, *hqxnames_left++);
  873. X        if (!(txtfile = fopen(txtfname, "r"))) {
  874. X            /* Maybe we are supposed to figure out the suffix ourselves? */
  875. X            strcat(txtfname, suffix);
  876. X            if (!(txtfile = fopen(txtfname, "r")))
  877. X                error("Cannot open %s", txtfname);
  878. X            }
  879. X
  880. X        if (stat(txtfname, &stbuf))
  881. X            error("Cannot read %s", txtfname);
  882. X
  883. X        /* stuff header data into the info header */
  884. X        bzero(&info, sizeof(info_header));
  885. X        info.nlen = strlen(txtfname);
  886. X        info.nlen = (info.nlen > NAMELEN) ? NAMELEN : info.nlen;
  887. X    info.name[info.nlen] = '\0';
  888. X        strcpy(info.name, txtfname);           /* name */
  889. X        mtim = time2mac(stbuf.st_mtime);
  890. X        ctim = time2mac(stbuf.st_ctime);
  891. X        bcopy(&mtim, info.mtim, 4);
  892. X        bcopy(&ctim, info.ctim, 4);
  893. X    info.uploadvers = '\201';
  894. X    info.readvers = '\201';
  895. X
  896. X        if (mode == RSRC) {
  897. X            /* dlen is already zero */
  898. X            rlen = long2mac(stbuf.st_size);
  899. X            bcopy(&rlen, info.rlen, 4);
  900. X            bcopy("APPL", info.type, 4);
  901. X            bcopy("CCOM", info.auth, 4);
  902. X            }
  903. X        else {
  904. X            dlen = long2mac(stbuf.st_size);
  905. X            bcopy(&dlen, info.dlen, 4);
  906. X            /* rlen is already zero */
  907. X            bcopy("TEXT", info.type, 4);
  908. X            if (mode == DATA) bcopy("????", info.auth, 4);
  909. X            else bcopy(text_author, info.auth, 4);
  910. X            }
  911. X
  912. X    /* calculate CRC */
  913. X    crc = calc_mb_crc(&info, 124, 0);
  914. X    info.crc[0] = (char) (crc >> 8);
  915. X    info.crc[1] = (char) crc;
  916. X
  917. X        /* Create the .bin file and write the info to it */
  918. X        sprintf(binfname, "%s/%s%s", dir, txtfname, ext);
  919. X        if ((binfile = fopen(binfname, "w")) == NULL)
  920. X            error("Cannot open %s", binfname);
  921. X        fprintf(verbose,
  922. X                "Converting     %-30s type = \"%4.4s\", author = \"%4.4s\"\n",
  923. X                txtfname, info.type, info.auth);
  924. X        fwrite(&info, sizeof(info), 1, binfile);
  925. X
  926. X        nchars = stbuf.st_size;
  927. X        extra_chars = 127 - (nchars+127) % 128;
  928. X        if (mode == TEXT) while (nchars--) {
  929. X            b = getc(txtfile);
  930. X            if (b == LF) b = CR;
  931. X            putc(b, binfile);
  932. X            }
  933. X        else while (nchars--) putc(getc(txtfile), binfile);
  934. X
  935. X        while (extra_chars--) putc(0, binfile);
  936. X        fclose(binfile);
  937. X        fclose(txtfile);
  938. X        }
  939. X    }
  940. X
  941. X/* This procedure copies the input file to the output file, basically, although
  942. X    in TEXT mode it changes CR's to LF's and in any mode it skips over the Mac
  943. X    info header. */
  944. X
  945. Xre_other(mode)
  946. Xint mode;
  947. X{   register ulong b;
  948. X    register ulong nchars;
  949. X    char txtfname[BINNAMELEN], binfname[BINNAMELEN];
  950. X    FILE *txtfile, *binfile; 
  951. X    char *suffix;
  952. X    info_header info;
  953. X
  954. X    if (mode == DATA) suffix = ".data";
  955. X    else if (mode == RSRC) suffix = ".rsrc";
  956. X    else suffix = ".text";
  957. X
  958. X    while (hqxnames_left[0][0] != '-') {
  959. X
  960. X        strcpy(binfname, *hqxnames_left++);
  961. X        if ((binfile = fopen(binfname, "r")) == NULL) {
  962. X            /* Maybe we are supposed to figure out the suffix ourselves? */
  963. X            strcat(binfname, ext);
  964. X            if (!(binfile = fopen(binfname, "r")))
  965. X                error("Cannot open %s", binfname);
  966. X            }
  967. X
  968. X        /* Read the info from the .bin file, create the output file */
  969. X        fread(&info, sizeof(info), 1, binfile);
  970. X        strncpy(txtfname, info.name, info.nlen);
  971. X    txtfname[info.nlen] = '\0';
  972. X        fprintf(verbose,
  973. X                "Converting     %-30s type = \"%4.4s\", author = \"%4.4s\"\n",
  974. X                txtfname, info.type, info.auth);
  975. X        if ((txtfile = fopen(txtfname, "r")) == NULL) {
  976. X            if ((txtfile = fopen(txtfname, "w")) == NULL)
  977. X                error("Cannot open %s", txtfname);
  978. X            }
  979. X        else {
  980. X            fclose(txtfile);
  981. X            strcat(txtfname, suffix);
  982. X            if ((txtfile = fopen(txtfname, "w")) == NULL)
  983. X                error("Cannot open %s", txtfname);
  984. X            }
  985. X
  986. X        nchars = mac2long(* (ulong *) info.dlen);
  987. X        if (mode == TEXT) while (nchars--) {
  988. X            b = getc(binfile);
  989. X            if (b == CR) b = LF;
  990. X            putc(b, txtfile);
  991. X            }
  992. X        else if (mode == DATA) while (nchars--)
  993. X            putc(getc(binfile), txtfile);
  994. X        else {
  995. X            while (nchars--) getc(binfile);
  996. X            nchars = mac2long(* (ulong *) info.rlen);
  997. X            while (nchars--) putc(getc(binfile), txtfile);
  998. X            }
  999. X
  1000. X        fclose(binfile);
  1001. X        fclose(txtfile);
  1002. X        }
  1003. X    }
  1004. END_OF_FILE
  1005. if test 13666 -ne `wc -c <'mcvert/mcvert.c'`; then
  1006.     echo shar: \"'mcvert/mcvert.c'\" unpacked with wrong size!
  1007. fi
  1008. # end of 'mcvert/mcvert.c'
  1009. fi
  1010. if test -f 'stuffit/sit.c' -a "${1}" != "-c" ; then 
  1011.   echo shar: Will not clobber existing file \"'stuffit/sit.c'\"
  1012. else
  1013. echo shar: Extracting \"'stuffit/sit.c'\" \(12417 characters\)
  1014. sed "s/^X//" >'stuffit/sit.c' <<'END_OF_FILE'
  1015. X/*
  1016. X * sit - Stuffit for UNIX
  1017. X *  Puts unix data files into stuffit archive suitable for downloading
  1018. X *    to a Mac.  Automatically processes files output from xbin.
  1019. X *
  1020. X *  Reverse engineered from unsit by Allan G. Weber, which was based on
  1021. X *  macput, which was based on ...
  1022. X *  Just like unsit this uses the host's version of compress to do the work.
  1023. X *
  1024. X * Examples:
  1025. X *   1) take collection of UNIX text files and make them LSC text files 
  1026. X *    when uncompressed on the mac:
  1027. X *       sit -u -T TEXT -C KAHL file ...
  1028. X *   2) Process output from xbin:
  1029. X *       xbin file1     (produces FileOne.{info,rsrc,data})
  1030. X *       sit file1
  1031. X *
  1032. X *  Tom Bereiter
  1033. X *    ..!{rutgers,ames}!cs.utexas.edu!halley!rolex!twb
  1034. X *
  1035. X * This version for CAP aufs files based on info from aufs source + mcvert etc.
  1036. X * Aufs version is program is called AUFSNAME (default stuffit)
  1037. X *
  1038. X * Aug 90. Nigel Perry, np@doc.ic.ac.uk
  1039. X *
  1040. X */
  1041. X#define BSD
  1042. X
  1043. X#include <sys/types.h>
  1044. X#include <sys/stat.h>
  1045. X#include <stdio.h>
  1046. X#include "sit.h"
  1047. X#ifdef BSD
  1048. X#include <sys/time.h>
  1049. X#include <sys/timeb.h>
  1050. X#else
  1051. X#include <time.h>
  1052. Xextern long timezone;
  1053. X#endif
  1054. X
  1055. X#ifndef min
  1056. X#define min(a,b) ((a)<(b)?(a):(b))
  1057. X#endif
  1058. X
  1059. X/* Mac time of 00:00:00 GMT, Jan 1, 1970 */
  1060. X#define TIMEDIFF 0x7c25b080
  1061. X
  1062. X/* if called by this name, program will work on aufs files */
  1063. X#define AUFSNAME "stuffit"
  1064. X
  1065. Xstruct sitHdr sh;
  1066. Xstruct fileHdr fh;
  1067. X
  1068. Xchar buf[BUFSIZ];
  1069. Xchar *defoutfile = "archive.sit";
  1070. Xint ofd;
  1071. Xushort crc;
  1072. Xint clen;
  1073. Xint rmfiles;
  1074. Xint    unixf;
  1075. Xchar *Creator, *Type;
  1076. Xint aufs;
  1077. X
  1078. Xusage() { fprintf(stderr,"Usage: sit file\n"); }
  1079. Xextern char *optarg;
  1080. Xextern int optind;
  1081. X
  1082. X/********************************************************************************/
  1083. X/* added for aufs, nicked from various places... */
  1084. X
  1085. X/* following from mcvert program */
  1086. X
  1087. X/* Useful, though not particularly Mac related, values */
  1088. Xtypedef unsigned char byte;     /* one byte, obviously */
  1089. Xtypedef unsigned short word;    /* must be 2 bytes */
  1090. Xtypedef unsigned long ulong;    /* 4 bytes */
  1091. X
  1092. X#define NAMELEN 63              /* maximum legal Mac file name length */
  1093. X
  1094. X/* Format of a bin file:
  1095. XA bin file is composed of 128 byte blocks.  The first block is the
  1096. Xinfo_header (see below).  Then comes the data fork, null padded to fill the
  1097. Xlast block.  Then comes the resource fork, padded to fill the last block.  A
  1098. Xproposal to follow with the text of the Get Info box has not been implemented,
  1099. Xto the best of my knowledge.  Version, zero1 and zero2 are what the receiving
  1100. Xprogram looks at to determine if a MacBinary transfer is being initiated.
  1101. X*/ 
  1102. Xtypedef struct {     /* info file header (128 bytes). Unfortunately, these
  1103. X                        longs don't align to word boundaries */
  1104. X            byte version;           /* there is only a version 0 at this time */
  1105. X            byte nlen;              /* Length of filename. */
  1106. X            byte name[NAMELEN];     /* Filename (only 1st nlen are significant)*/
  1107. X            byte type[4];           /* File type. */
  1108. X            byte auth[4];           /* File creator. */
  1109. X            byte flags;             /* file flags: LkIvBnSyBzByChIt */
  1110. X            byte zero1;             /* Locked, Invisible,Bundle, System */
  1111. X                                    /* Bozo, Busy, Changed, Init */
  1112. X            byte icon_vert[2];      /* Vertical icon position within window */
  1113. X            byte icon_horiz[2];     /* Horizontal icon postion in window */
  1114. X            byte window_id[2];      /* Window or folder ID. */
  1115. X            byte protect;           /* = 1 for protected file, 0 otherwise */
  1116. X            byte zero2;
  1117. X            byte dflen[4];          /* Data Fork length (bytes) -   most sig.  */
  1118. X            byte rflen[4];          /* Resource Fork length         byte first */
  1119. X            byte cdate[4];          /* File's creation date. */
  1120. X            byte mdate[4];          /* File's "last modified" date. */
  1121. X            byte ilen[2];           /* GetInfo message length */
  1122. X        byte flags2;            /* Finder flags, bits 0-7 */
  1123. X        byte unused[14];       
  1124. X        byte packlen[4];        /* length of total files when unpacked */
  1125. X        byte headlen[2];        /* length of secondary header */
  1126. X        byte uploadvers;        /* Version of MacBinary II that the uploading program is written for */
  1127. X        byte readvers;          /* Minimum MacBinary II version needed to read this file */
  1128. X            byte crc[2];            /* CRC of the previous 124 bytes */
  1129. X        byte padding[2];        /* two trailing unused bytes */
  1130. X            } info_header;
  1131. X
  1132. X/* end of mcvert stuff */
  1133. X/* from CAP aufs documentation */
  1134. X
  1135. X#define FINFOLEN 32
  1136. X#define MAXCLEN 199
  1137. Xtypedef struct
  1138. X{  /* byte fi_fndr[FINFOLEN]; */    /* finder info */
  1139. X   /* what I think the above is... */
  1140. X   ulong fndr_type, fndr_creator;
  1141. X   word fndr_flags;
  1142. X   ulong fndr_loc;
  1143. X   word fndr_fldr;
  1144. X   word fndr_icon;
  1145. X   byte fndr_unused[8];
  1146. X   word fndr_comment;
  1147. X   ulong fndr_putaway;
  1148. X   /* end of fi_fndr */
  1149. X
  1150. X   word fi_attr;            /* attributes */
  1151. X#define FI_MAGIC1 255
  1152. X   byte fi_magic1;        /* was: length of comment */
  1153. X#define FI_VERSION 0x10        /* version major 1, minor 0 */
  1154. X                /* if more than 8 versions then */
  1155. X                /* something wrong anyway */
  1156. X   byte fi_version;        /* version number */
  1157. X#define FI_MAGIC 0xda
  1158. X   byte fi_magic;        /* magic word check */
  1159. X   byte fi_bitmap;        /* bitmap of included info */
  1160. X#define FI_BM_SHORTFILENAME 0x1    /* is this included? */
  1161. X#define FI_BM_MACINTOSHFILENAME 0x2 /* is this included? */
  1162. X   byte fi_shortfilename[12+1];    /* possible short file name */
  1163. X   byte fi_macfilename[32+1];    /* possible macintosh file name */
  1164. X   byte fi_comln;        /* comment length */
  1165. X   byte fi_comnt[MAXCLEN+1];    /* comment string */
  1166. X} FileInfo;
  1167. X
  1168. XFileInfo fndr_info;
  1169. X
  1170. X/* end aufs */
  1171. X/********************************************************************************/
  1172. X
  1173. Xmain(argc,argv) char **argv; {
  1174. X    int i,n;
  1175. X    int total, nfiles;
  1176. X    int c;
  1177. X
  1178. X    rmfiles = unixf = 0;
  1179. X    aufs = strcmp(argv[0], AUFSNAME) == 0;
  1180. X
  1181. X    while ((c=getopt(argc, argv, "ro:uC:T:")) != EOF)
  1182. X    switch (c) {
  1183. X        case 'r':
  1184. X            rmfiles++;    /* remove files when done */
  1185. X            break;
  1186. X        case 'o':        /* specify output file */
  1187. X            defoutfile = optarg;
  1188. X            break;
  1189. X        case 'u':        /* unix file -- change '\n' to '\r' */
  1190. X            unixf++;
  1191. X            break;
  1192. X        case 'C':        /* set Mac creator */
  1193. X            Creator = optarg;
  1194. X            break;
  1195. X        case 'T':        /* set Mac file type */
  1196. X            Type = optarg;
  1197. X            break;
  1198. X        case '?':
  1199. X            usage();
  1200. X            exit(1);
  1201. X    }
  1202. X
  1203. X    if(aufs && (strlen(defoutfile) > 32))
  1204. X    {   fprintf(stderr, "Output name must not exceed 32 characters: %s\n", defoutfile);
  1205. X        exit(-1);
  1206. X    }
  1207. X
  1208. X    if(aufs)
  1209. X    {    /* make the .finderinfo file */
  1210. X        char buf[32+12+1];
  1211. X
  1212. X        strcpy(buf, ".finderinfo/");
  1213. X        strcat(buf, defoutfile);
  1214. X        if ((ofd=creat(buf,0644))<0)
  1215. X        {   perror(buf);
  1216. X            exit(1);
  1217. X        }
  1218. X        bzero(&fndr_info, sizeof(FileInfo));
  1219. X        bcopy("SIT!", &fndr_info.fndr_type, 4);
  1220. X        bcopy("SIT!", &fndr_info.fndr_creator, 4);
  1221. X        fndr_info.fi_magic1 = FI_MAGIC1;
  1222. X        fndr_info.fi_version = FI_VERSION;
  1223. X        fndr_info.fi_magic = FI_MAGIC;
  1224. X        fndr_info.fi_bitmap = FI_BM_MACINTOSHFILENAME;
  1225. X        strcpy(fndr_info.fi_macfilename, defoutfile);
  1226. X        write(ofd, &fndr_info, sizeof(FileInfo));
  1227. X        close(ofd);
  1228. X    }
  1229. X
  1230. X    if ((ofd=creat(defoutfile,0644))<0) {
  1231. X        perror(defoutfile);
  1232. X        exit(1);
  1233. X    }
  1234. X    /* empty header, will seek back and fill in later */
  1235. X    write(ofd,&sh,sizeof sh);
  1236. X
  1237. X    for (i=optind; i<argc; i++) {
  1238. X        n = put_file(argv[i]);
  1239. X        if (n) {
  1240. X            total += n;
  1241. X            nfiles++;
  1242. X        }
  1243. X    }
  1244. X    lseek(ofd,0,0);
  1245. X
  1246. X    total += sizeof(sh);
  1247. X    /* header header */
  1248. X    strncpy(sh.sig1,"SIT!",4);
  1249. X    cp2(nfiles,sh.numFiles);
  1250. X    cp4(total,sh.arcLen);
  1251. X    strncpy(sh.sig2,"rLau",4);
  1252. X    sh.version = 1;
  1253. X
  1254. X    write(ofd,&sh,sizeof sh);
  1255. X}
  1256. X
  1257. Xput_file(name)
  1258. Xchar name[];
  1259. X{
  1260. X    struct stat st;
  1261. X    struct infohdr ih;
  1262. X    int i,n,fd;
  1263. X    long fpos1, fpos2;
  1264. X    char nbuf[256], *p;
  1265. X    int fork=0;
  1266. X    long tdiff;
  1267. X    struct tm *tp;
  1268. X#ifdef BSD
  1269. X    struct timeb tbuf;
  1270. X#else
  1271. X    long bs;
  1272. X#endif
  1273. X
  1274. X    fpos1 = lseek(ofd,0,1); /* remember where we are */
  1275. X    /* write empty header, will seek back and fill in later */
  1276. X    bzero(&fh,sizeof fh);
  1277. X    write(ofd,&fh,sizeof fh);
  1278. X
  1279. X    /* look for resource fork */
  1280. X    if(aufs)
  1281. X    {   strcpy(nbuf, ".resource/");
  1282. X        strcat(nbuf, name);
  1283. X    }
  1284. X    else
  1285. X    {   strcpy(nbuf,name);
  1286. X        strcat(nbuf,".rsrc");
  1287. X    }
  1288. X    if (stat(nbuf,&st)>=0 && st.st_size) {    /* resource fork exists */
  1289. X        dofork(nbuf);
  1290. X        cp4(st.st_size,fh.rLen);
  1291. X        cp4(clen,fh.cRLen);
  1292. X        cp2(crc,fh.rsrcCRC);
  1293. X        fh.compRMethod = lpzComp;
  1294. X        fork++;
  1295. X    }
  1296. X    if (rmfiles) unlink(nbuf);    /* ignore errors */
  1297. X
  1298. X    /* look for data fork */
  1299. X    st.st_size = 0;
  1300. X    strcpy(nbuf,name);
  1301. X    if (stat(nbuf,&st)<0) {        /* first try plain name */
  1302. X        strcat(nbuf,".data");
  1303. X        stat(nbuf,&st);
  1304. X    }
  1305. X    if (st.st_size) {        /* data fork exists */
  1306. X        dofork(nbuf);
  1307. X        cp4(st.st_size,fh.dLen);
  1308. X        cp4(clen,fh.cDLen);
  1309. X        cp2(crc,fh.dataCRC);
  1310. X        fh.compDMethod = lpzComp;
  1311. X        fork++;
  1312. X    }
  1313. X    if (fork == 0) {
  1314. X        fprintf(stderr,"%s: no data or resource files\n",name);
  1315. X        return 0;
  1316. X    }
  1317. X    if (rmfiles) unlink(nbuf);    /* ignore errors */
  1318. X
  1319. X    /* look for .info file */
  1320. X    if(aufs)
  1321. X    {   strcpy(nbuf, ".finderinfo/");
  1322. X        strcat(nbuf, name);
  1323. X    }
  1324. X    else
  1325. X    {   strcpy(nbuf,name);
  1326. X        strcat(nbuf,".info");
  1327. X    }
  1328. X    if((fd=open(nbuf,0))>=0
  1329. X       && ((!aufs && read(fd,&ih,sizeof(ih))==sizeof(ih))
  1330. X           || (aufs && read(fd,&fndr_info,sizeof(FileInfo))==sizeof(FileInfo))
  1331. X          )
  1332. X      )
  1333. X    {   if(aufs)
  1334. X        {   char *np;
  1335. X        
  1336. X        np = (char *)(fndr_info.fi_bitmap & FI_BM_MACINTOSHFILENAME ? fndr_info.fi_macfilename
  1337. X                                        : fndr_info.fi_shortfilename);
  1338. X        fh.fName[0] = (char)strlen(np);
  1339. X        strncpy(fh.fName+1, np, 64);
  1340. X        bcopy(&fndr_info.fndr_type, fh.fType, 4);
  1341. X        bcopy(&fndr_info.fndr_creator, fh.fCreator, 4);
  1342. X        bcopy(&fndr_info.fndr_flags, fh.FndrFlags, 2);
  1343. X#ifdef BSD
  1344. X        ftime(&tbuf);
  1345. X        tp = localtime(&tbuf.time);
  1346. X        tdiff = TIMEDIFF - tbuf.timezone * 60;
  1347. X        if (tp->tm_isdst)
  1348. X            tdiff += 60 * 60;
  1349. X#else
  1350. X        /* I hope this is right! -andy */
  1351. X        time(&bs);
  1352. X        tp = localtime(&bs);
  1353. X        tdiff = TIMEDIFF - timezone;
  1354. X        if (tp->tm_isdst)
  1355. X            tdiff += 60 * 60;
  1356. X#endif
  1357. X        cp4(st.st_ctime + tdiff, fh.cDate);
  1358. X        cp4(st.st_mtime + tdiff, fh.mDate);
  1359. X        }
  1360. X        else
  1361. X        {   strncpy(fh.fName, ih.name,64);
  1362. X        strncpy(fh.fType, ih.type, 4);
  1363. X        strncpy(fh.fCreator, ih.creator, 4);
  1364. X        strncpy(fh.FndrFlags, ih.flag, 2);
  1365. X        strncpy(fh.cDate, ih.ctime, 4);
  1366. X        strncpy(fh.mDate, ih.mtime, 4);
  1367. X        }
  1368. X    }
  1369. X    else {    /* no info file so fake it */
  1370. X        strncpy(&fh.fName[1], name,63); fh.fName[0] = min(strlen(name),63);
  1371. X        /* default to LSC text file */
  1372. X        strncpy(fh.fType, Type ? Type : "TEXT", 4);
  1373. X        strncpy(fh.fCreator, Creator ? Creator : "KAHL", 4);
  1374. X        /* convert unix file time to mac time format */
  1375. X#ifdef BSD
  1376. X        ftime(&tbuf);
  1377. X        tp = localtime(&tbuf.time);
  1378. X        tdiff = TIMEDIFF - tbuf.timezone * 60;
  1379. X        if (tp->tm_isdst)
  1380. X            tdiff += 60 * 60;
  1381. X#else
  1382. X        /* I hope this is right! -andy */
  1383. X        time(&bs);
  1384. X        tp = localtime(&bs);
  1385. X        tdiff = TIMEDIFF - timezone;
  1386. X        if (tp->tm_isdst)
  1387. X            tdiff += 60 * 60;
  1388. X#endif
  1389. X        cp4(st.st_ctime + tdiff, fh.cDate);
  1390. X        cp4(st.st_mtime + tdiff, fh.mDate);
  1391. X    }
  1392. X    close(fd);
  1393. X    if (rmfiles) unlink(nbuf);    /* ignore errors */
  1394. X
  1395. X    crc = updcrc(0,&fh,(sizeof fh)-2);
  1396. X    cp2(crc, fh.hdrCRC);
  1397. X
  1398. X    fpos2 = lseek(ofd,0,1);        /* remember where we are */
  1399. X    lseek(ofd,fpos1,0);                /* seek back over file(s) and header */
  1400. X    write(ofd,&fh,sizeof fh);        /* write back header */
  1401. X    fpos2=lseek(ofd,fpos2,0);                /* seek forward file */
  1402. X
  1403. X    return (fpos2 - fpos1);
  1404. X}
  1405. X    
  1406. Xdofork(name)
  1407. Xchar name[];
  1408. X{
  1409. X    FILE *fs;
  1410. X    int n, fd, ufd;
  1411. X    char *p;
  1412. X
  1413. X    if ((fd=open(name,0))<0) {
  1414. X        perror(name);
  1415. X        return 0;
  1416. X    }   
  1417. X    if (unixf)        /* build conversion file */
  1418. X        if ((ufd=creat("sit+temp",0644))<0) {
  1419. X            perror("sit+temp");
  1420. X            return 0;
  1421. X        }   
  1422. X    /* do crc of file: */
  1423. X    crc = 0;
  1424. X    while ((n=read(fd,buf,BUFSIZ))>0) {
  1425. X        if (unixf) {    /* convert '\n' to '\r' */
  1426. X            for (p=buf; p<&buf[n]; p++)
  1427. X                if (*p == '\n') *p = '\r';
  1428. X            write(ufd,buf,n);
  1429. X        }
  1430. X        crc = updcrc(crc,buf,n);
  1431. X    }
  1432. X    close(fd);
  1433. X    /*
  1434. X     * open pipe to compress file
  1435. X     *   If a unix file ('\n' -> '\r' conversion) 'sit+temp' will be a new copy
  1436. X     *   with the conversion done.    Otherwise, 'sit+temp' is just a link to 
  1437. X     *   the input file.
  1438. X     */
  1439. X    if (unixf)
  1440. X        close(ufd);
  1441. X    else link(name,"sit+temp");
  1442. X    fs = popen("compress -c -n -b 14 sit+temp","r");
  1443. X    if (fs == NULL) {
  1444. X        perror(name);
  1445. X        return 0;
  1446. X    }
  1447. X    /* write out compressed file */
  1448. X    clen = 0;
  1449. X    while ((n=fread(buf,1,BUFSIZ,fs))>0) {
  1450. X        write(ofd,buf,n);
  1451. X        clen += n;
  1452. X    }
  1453. X    pclose(fs);
  1454. X    unlink("sit+temp");
  1455. X}
  1456. X
  1457. Xcp2(x,dest)
  1458. Xunsigned short x;
  1459. Xchar dest[];
  1460. X{
  1461. X    dest[0] = x>>8;
  1462. X    dest[1] = x;
  1463. X}
  1464. X
  1465. Xcp4(x,dest)
  1466. Xunsigned long x;
  1467. Xchar dest[];
  1468. X{
  1469. X    dest[0] = x>>24;
  1470. X    dest[1] = x>>16;
  1471. X    dest[2] = x>>8;
  1472. X    dest[3] = x;
  1473. X}
  1474. END_OF_FILE
  1475. if test 12417 -ne `wc -c <'stuffit/sit.c'`; then
  1476.     echo shar: \"'stuffit/sit.c'\" unpacked with wrong size!
  1477. fi
  1478. # end of 'stuffit/sit.c'
  1479. fi
  1480. echo shar: End of archive 3 \(of 4\).
  1481. cp /dev/null ark3isdone
  1482. MISSING=""
  1483. for I in 1 2 3 4 ; do
  1484.     if test ! -f ark${I}isdone ; then
  1485.     MISSING="${MISSING} ${I}"
  1486.     fi
  1487. done
  1488. if test "${MISSING}" = "" ; then
  1489.     echo You have unpacked all 4 archives.
  1490.     rm -f ark[1-9]isdone
  1491. else
  1492.     echo You still need to unpack the following archives:
  1493.     echo "        " ${MISSING}
  1494. fi
  1495. ##  End of shell archive.
  1496. exit 0
  1497. --- end of part 3 ---
  1498.