[net.sources.mac] xbin: here it is

vishniac@wanginst.UUCP (Ephraim Vishniac) (07/23/85)

Here's xbin, the Unix equivalent of binhex 4 (decoding part only).
This posting includes Dan LaLiberte's performance improvements.  I've
also tweaked some of the declarations to cut down the noise from lint.

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	xbin.c
#	xbin.l
# This archive created: Tue Jul 23 12:52:31 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(922 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^	X//' << \SHAR_EOF > 'README'
	XThis version of "xbin" can handle all three BinHex formats
	X(so far).  Thanks to Darin Adler at TMQ Software for providing
	Xthe code to compute and check the CRC values for all three formats.
	X
	XOther new features include "list" and "verbose" modes, the
	Xability to convert several binhex files at one time, the ability
	Xto read standard input, somewhat better error handling, and a
	Xmanual page.
	X
	XAny extraneous mail or news headers are ignored, but xbin relies
	Xon finding a line which starts with "(This file" to know when
	Xthe header ends and the good stuff begins.  You can add one
	Xof these by hand if it's been lost.
	X
	XTo compile it on USG systems, type:
	X	cc -o xbin xbin.c
	X
	Xor on Berkeley systems:
	X	cc -o xbin xbin.c -DBSD
	X
	XAs usual, please report any problems, suggestions, or
	Ximprovements to me.
	X
	X	Dave Johnson
	X	Brown University Computer Science
	X	ddj%brown@csnet-relay.ARPA
	X	{ihnp4,decvax,allegra,ulysses,linus}!brunix!ddj
SHAR_EOF
if test 922 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 922 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'xbin.c'" '(17305 characters)'
if test -f 'xbin.c'
then
	echo shar: will not over-write existing file "'xbin.c'"
else
sed 's/^	X//' << \SHAR_EOF > 'xbin.c'
	X#ifndef lint
	Xstatic char version[] = "xbin.c Version 2.1 03/26/85";
	X#endif lint
	X
	X#include <stdio.h>
	X#include <sys/types.h>
	X#include <sys/stat.h>
	X#include <sys/dir.h>
	X
	X#ifdef MAXNAMLEN	/* 4.2 BSD */
	X#define FNAMELEN MAXNAMLEN
	X#else
	X#define FNAMELEN DIRSIZ
	X#endif
	X
	X#ifdef BSD
	X#include <sys/time.h>
	X#include <sys/timeb.h>
	X#define search_last rindex
	Xextern char *rindex();
	X#else
	X#include <time.h>
	Xextern long timezone;
	X#define search_last strrchr
	Xextern char *strrchr();
	X#endif
	X
	X/* Mac time of 00:00:00 GMT, Jan 1, 1970 */
	X#define TIMEDIFF 0x7c25b080
	X
	X#define DATABYTES 128
	X
	X#define BYTEMASK 0xff
	X#define BYTEBIT 0x100
	X#define WORDMASK 0xffff
	X#define WORDBIT 0x10000
	X
	X#define NAMEBYTES 63
	X#define H_NLENOFF 1
	X#define H_NAMEOFF 2
	X
	X/* 65 <-> 80 is the FInfo structure */
	X#define H_TYPEOFF 65
	X#define H_AUTHOFF 69
	X#define H_FLAGOFF 73
	X
	X#define H_LOCKOFF 81
	X#define H_DLENOFF 83
	X#define H_RLENOFF 87
	X#define H_CTIMOFF 91
	X#define H_MTIMOFF 95
	X
	X#define H_OLD_DLENOFF 81
	X#define H_OLD_RLENOFF 85
	X
	X#define F_BUNDLE 0x2000
	X#define F_LOCKED 0x8000
	X
	Xstruct macheader {
	X	char m_name[NAMEBYTES+1];
	X	char m_type[4];
	X	char m_author[4];
	X	short m_flags;
	X	long m_datalen;
	X	long m_rsrclen;
	X	long m_createtime;
	X	long m_modifytime;
	X} mh;
	X
	Xstruct filenames {
	X	char f_info[256];
	X	char f_data[256];
	X	char f_rsrc[256];
	X} files;
	X
	Xint pre_beta;	/* options */
	Xint listmode;
	Xint verbose;
	X
	Xint compressed;	/* state variables */
	Xint qformat;
	XFILE *ifp;
	X
	X/*
	X * xbin -- unpack BinHex format file into suitable
	X * format for downloading with macput
	X * Dave Johnson, Brown University Computer Science
	X *
	X * checksum code by Darin Adler, TMQ Software
	X *
	X * (c) 1984 Brown University
	X * may be used but not sold without permission
	X *
	X * created ddj 12/16/84
	X * revised ddj 03/10/85 -- version 4.0 compatibility, other minor mods
	X * revised ddj 03/11/85 -- strip LOCKED bit from m_flags
	X * revised ahm 03/12/85 -- System V compatibility
	X * revised dba 03/16/85 -- 4.0 EOF fixed, 4.0 checksum added
	X * revised ddj 03/17/85 -- extend new features to older formats: -l, stdin
	X * revised ddj 03/24/85 -- check for filename truncation, allow multiple files
	X * revised ddj 03/26/85 -- fixed USG botches, many problems w/multiple files
	X */
	Xchar usage[] = "usage: \"xbin [-v] [-l] [-o] [-n name] [-] filename\"\n";
	X
	Xmain(ac, av)
	Xchar **av;
	X{
	X	char *filename, *macname;
	X
	X	filename = ""; macname = "";
	X	ac--; av++;
	X	while (ac) {
	X		if (av[0][0] == '-') {
	X			switch (av[0][1]) {
	X			case '\0':
	X				filename = "-";
	X				break;
	X			case 'v':
	X				verbose++;
	X				break;
	X			case 'l':
	X				listmode++;
	X				break;
	X			case 'o':
	X				pre_beta++;
	X				break;
	X			case 'n':
	X				if (ac > 1) {
	X					ac--; av++;
	X					macname = av[0];
	X					filename = "";
	X					break;
	X				}
	X				else
	X					goto bad_usage;
	X			default:
	X				goto bad_usage;
	X			}
	X		}
	X		else
	X			filename = av[0];
	X		if (filename[0] != '\0') {
	X			setup_files(filename, macname);
	X			if (listmode) {
	X				print_header();
	X			}
	X			else {
	X				process_forks();
	X				/* now that we know the size of the forks */
	X				forge_info();
	X			}
	X			if (ifp != stdin)
	X				fclose(ifp);
	X			macname = "";
	X			ifp = NULL;		/* reset state */
	X			qformat = 0;
	X			compressed = 0;
	X		}
	X		ac--; av++;
	X	}
	X	if (*filename == '\0') {
	Xbad_usage:
	X		fprintf(stderr, usage);
	X		exit(1);
	X	}
	X}
	X
	Xstatic char *extensions[] = {
	X	".hqx",
	X	".hcx",
	X	".hex",
	X	"",
	X	NULL
	X};
	X
	Xsetup_files(filename, macname)
	Xchar *filename;		/* input file name -- extension optional */
	Xchar *macname;		/* name to use on the mac side of things */
	X{
	X	char namebuf[256], *np;
	X	char **ep;
	X	int n;
	X	struct stat stbuf;
	X	long curtime;
	X
	X	if (filename[0] == '-') {
	X		ifp = stdin;
	X		filename = "stdin";
	X	}
	X	else {
	X		/* find input file and open it */
	X		for (ep = extensions; *ep != NULL; ep++) {
	X			sprintf(namebuf, "%s%s", filename, *ep);
	X			if (stat(namebuf, &stbuf) == 0)
	X				break;
	X		}
	X		if (*ep == NULL) {
	X			perror(namebuf);
	X			exit(-1);
	X		}
	X		ifp = fopen(namebuf, "r");
	X		if (ifp == NULL) {
	X			perror(namebuf);
	X			exit(-1);
	X		}
	X	}
	X	if (ifp == stdin) {
	X		curtime = time(0);
	X		mh.m_createtime = curtime;
	X		mh.m_modifytime = curtime;
	X	}
	X	else {
	X		mh.m_createtime = stbuf.st_mtime;
	X		mh.m_modifytime = stbuf.st_mtime;
	X	}
	X	if (listmode || verbose) {
	X		fprintf(stderr, "%s %s%s",
	X			listmode ? "\nListing" : "Converting",
	X			namebuf, listmode ? ":\n" : " ");
	X	}
	X
	X	qformat = find_header(); /* eat mailer header &cetera, intuit format */
	X
	X	if (qformat)
	X		do_q_header(macname);
	X	else
	X		do_o_header(macname, filename);
	X
	X	/* make sure host file name doesn't get truncated beyond recognition */
	X	n = strlen(mh.m_name);
	X	if (n > FNAMELEN - 2)
	X		n = FNAMELEN - 2;
	X	strncpy(namebuf, mh.m_name, n);
	X	namebuf[n] = '\0';
	X
	X	/* get rid of troublesome characters */
	X	for (np = namebuf; *np; np++)
	X		if (*np == ' ' || *np == '/')
	X			*np = '_';
	X
	X	sprintf(files.f_data, "%s.data", namebuf);
	X	sprintf(files.f_rsrc, "%s.rsrc", namebuf);
	X	sprintf(files.f_info, "%s.info", namebuf);
	X	if (verbose)
	X		fprintf(stderr, "==> %s.{info,data,rsrc}\n", namebuf);
	X}
	X
	X/* print out header information in human-readable format */
	Xprint_header()
	X{
	X	char *ctime();
	X
	X	printf("macname: %s\n", mh.m_name);
	X	printf("filetype: %.4s, ", mh.m_type);
	X	printf("author: %.4s, ", mh.m_author);
	X	printf("flags: 0x%x\n", mh.m_flags);
	X	if (qformat) {
	X		printf("data length: %d, ", mh.m_datalen);
	X		printf("rsrc length: %d\n", mh.m_rsrclen);
	X	}
	X	if (!pre_beta) {
	X		printf("create time: %s", ctime(&mh.m_createtime));
	X	}
	X}
	X
	Xprocess_forks()
	X{
	X	if (qformat) {
	X		/* read data and resource forks of .hqx file */
	X		do_q_fork(files.f_data, mh.m_datalen);
	X		do_q_fork(files.f_rsrc, mh.m_rsrclen);
	X	}
	X	else
	X		do_o_forks();
	X}
	X
	X/* write out .info file from information in the mh structure */
	Xforge_info()
	X{
	X	static char buf[DATABYTES];
	X	char *np;
	X	FILE *fp;
	X	int n, tdiff;
	X	struct tm *tp;
	X#ifdef BSD
	X	struct timeb tbuf;
	X#else
	X	long bs;
	X#endif
	X
	X	for (np = mh.m_name; *np; np++)
	X		if (*np == '_') *np = ' ';
	X
	X	buf[H_NLENOFF] = n = np - mh.m_name;
	X	strncpy(buf + H_NAMEOFF, mh.m_name, n);
	X	strncpy(buf + H_TYPEOFF, mh.m_type, 4);
	X	strncpy(buf + H_AUTHOFF, mh.m_author, 4);
	X	put2(buf + H_FLAGOFF, mh.m_flags & ~F_LOCKED);
	X	if (pre_beta) {
	X		put4(buf + H_OLD_DLENOFF, mh.m_datalen);
	X		put4(buf + H_OLD_RLENOFF, mh.m_rsrclen);
	X	}
	X	else {
	X		put4(buf + H_DLENOFF, mh.m_datalen);
	X		put4(buf + H_RLENOFF, mh.m_rsrclen);
	X
	X		/* convert unix file time to mac time format */
	X#ifdef BSD
	X		ftime(&tbuf);
	X		tp = localtime(&tbuf.time);
	X		tdiff = TIMEDIFF - tbuf.timezone * 60;
	X		if (tp->tm_isdst)
	X			tdiff += 60 * 60;
	X#else
	X		/* I hope this is right! -andy */
	X		time(&bs);
	X		tp = localtime(&bs);
	X		tdiff = TIMEDIFF - timezone;
	X		if (tp->tm_isdst)
	X			tdiff += 60 * 60;
	X#endif
	X		put4(buf + H_CTIMOFF, mh.m_createtime + tdiff);
	X		put4(buf + H_MTIMOFF, mh.m_modifytime + tdiff);
	X	}
	X	fp = fopen(files.f_info, "w");
	X	if (fp == NULL) {
	X		perror("info file");
	X		exit(-1);
	X	}
	X	fwrite(buf, 1, DATABYTES, fp);
	X	fclose(fp);
	X}
	X
	X/* eat characters until header detected, return which format */
	Xfind_header()
	X{
	X	int c, at_bol;
	X	char ibuf[BUFSIZ];
	X
	X	/* look for "(This file ...)" line */
	X	while (fgets(ibuf, BUFSIZ, ifp) != NULL) {
	X		if (strncmp(ibuf, "(This file", 10) == 0)
	X			break;
	X	}
	X	at_bol = 1;
	X	while ((c = getc(ifp)) != EOF) {
	X		switch (c) {
	X		case '\n':
	X		case '\r':
	X			at_bol = 1;
	X			break;
	X		case ':':
	X			if (at_bol)	/* q format */
	X				return 1;
	X			break;
	X		case '#':
	X			if (at_bol) {	/* old format */
	X				ungetc(c, ifp);
	X				return 0;
	X			}
	X			break;
	X		default:
	X			at_bol = 0;
	X			break;
	X		}
	X	}
	X
	X	fprintf(stderr, "unexpected EOF\n");
	X	exit(2);
	X}
	X
	Xstatic unsigned int crc;
	X
	Xshort get2q();
	Xlong get4q();
	X
	X/* read header of .hqx file */
	Xdo_q_header(macname)
	Xchar *macname;
	X{
	X	char namebuf[256];		/* big enough for both att & bsd */
	X	int n;
	X	unsigned int calc_crc, file_crc;
	X
	X	crc = 0;			/* compute a crc for the header */
	X	q_init();			/* reset static variables */
	X
	X	n = getq();			/* namelength */
	X	n++;				/* must read trailing null also */
	X	getqbuf(namebuf, n);		/* read name */
	X	if (macname[0] == '\0')
	X		macname = namebuf;
	X
	X	n = strlen(macname);
	X	if (n > NAMEBYTES)
	X		n = NAMEBYTES;
	X	strncpy(mh.m_name, macname, n);
	X	mh.m_name[n] = '\0';
	X
	X	getqbuf(mh.m_type, 4);
	X	getqbuf(mh.m_author, 4);
	X	mh.m_flags = get2q();
	X	mh.m_datalen = get4q();
	X	mh.m_rsrclen = get4q();
	X
	X	comp_q_crc(0);
	X	comp_q_crc(0);
	X	calc_crc = crc;
	X	file_crc = get2q();
	X	verify_crc(calc_crc, file_crc);
	X}
	X
	Xdo_q_fork(fname, len)
	Xchar *fname;
	Xregister long len;
	X{
	X	FILE *outf;
	X	register int c, i;
	X	unsigned int calc_crc, file_crc;
	X
	X	outf = fopen(fname, "w");
	X	if (outf == NULL) {
	X		perror(fname);
	X		exit(-1);
	X	}
	X
	X	crc = 0;	/* compute a crc for a fork */
	X
	X	if (len)
	X		for (i = 0; i < len; i++) {
	X			if ((c = getq()) == EOF) {
	X				fprintf(stderr, "unexpected EOF\n");
	X				exit(2);
	X			}
	X			putc(c, outf);
	X		}
	X
	X	comp_q_crc(0);
	X	comp_q_crc(0);
	X	calc_crc = crc;
	X	file_crc = get2q();
	X	verify_crc(calc_crc, file_crc);
	X	fclose(outf);
	X}
	X
	X/* verify_crc(); -- check if crc's check out */
	Xverify_crc(calc_crc, file_crc)
	Xunsigned int calc_crc, file_crc;
	X{
	X	calc_crc &= WORDMASK;
	X	file_crc &= WORDMASK;
	X
	X	if (calc_crc != file_crc) {
	X		fprintf(stderr, "CRC error\n---------\n");
	X		fprintf(stderr, "CRC in file:\t0x%x\n", file_crc);
	X		fprintf(stderr, "calculated CRC:\t0x%x\n", calc_crc);
	X		exit(3);
	X	}
	X}
	X
	Xstatic int eof;
	Xstatic char obuf[3];
	Xstatic char *op, *oend;
	X
	X/* initialize static variables for q format input */
	Xq_init()
	X{
	X	eof = 0;
	X	op = obuf;
	X	oend = obuf + sizeof obuf;
	X}
	X
	X/* get2q(); q format -- read 2 bytes from input, return short */
	Xshort
	Xget2q()
	X{
	X	register int c;
	X	short value = 0;
	X
	X	c = getq();
	X	value = (c & BYTEMASK) << 8;
	X	c = getq();
	X	value |= (c & BYTEMASK);
	X
	X	return value;
	X}
	X
	X/* get4q(); q format -- read 4 bytes from input, return long */
	Xlong
	Xget4q()
	X{
	X	register int c, i;
	X	long value = 0;
	X
	X	for (i = 0; i < 4; i++) {
	X		c = getq();
	X		value <<= 8;
	X		value |= (c & BYTEMASK);
	X	}
	X	return value;
	X}
	X
	X/* getqbuf(); q format -- read n characters from input into buf */
	X/*		All or nothing -- no partial buffer allowed */
	Xgetqbuf(buf, n)
	Xregister char *buf;
	Xregister int n;
	X{
	X	register int c, i;
	X
	X	for (i = 0; i < n; i++) {
	X		if ((c = getq()) == EOF)
	X			return EOF;
	X		*buf++ = c;
	X	}
	X	return 0;
	X}
	X
	X#define RUNCHAR 0x90
	X
	X/* q format -- return one byte per call, keeping track of run codes */
	Xgetq()
	X{
	X	register int c;
	X
	X	if ((c = getq_nocrc()) == EOF)
	X		return EOF;
	X	comp_q_crc((unsigned)c);
	X	return c;
	X}
	X
	Xgetq_nocrc()
	X{
	X	static int rep, lastc;
	X	int c;
	X
	X	if (rep) {
	X		rep--;
	X		return lastc;
	X	}
	X	if ((c = getq_raw()) == EOF) {
	X		return EOF;
	X		}
	X	if (c == RUNCHAR) {
	X		if ((rep = getq_raw()) == EOF)
	X			return EOF;
	X		if (rep == 0) {
	X			return RUNCHAR;
	X		}
	X		else {
	X			/* already returned one, about to return another */
	X			rep -= 2;
	X			return lastc;
	X		}
	X	}
	X	else {
	X		lastc = c;
	X		return c;
	X	}
	X}
	X
	X/* q format -- return next 8 bits from file without interpreting run codes */
	Xgetq_raw()
	X{
	X	char ibuf[4];
	X	register char *ip = ibuf, *iend = ibuf + sizeof ibuf;
	X	int c;
	X
	X	if (op == obuf) {
	X		for (ip = ibuf; ip < iend; ip++) {
	X			if ((c = get6bits()) == EOF)
	X				if (ip <= &ibuf[1])
	X					return EOF;
	X				else if (ip == &ibuf[2])
	X					eof = 1;
	X				else
	X					eof = 2;
	X			*ip = c;
	X		}
	X		obuf[0] = (ibuf[0] << 2 | ibuf[1] >> 4);
	X		obuf[1] = (ibuf[1] << 4 | ibuf[2] >> 2);
	X		obuf[2] = (ibuf[2] << 6 | ibuf[3]);
	X	}
	X	if ((eof) & (op >= &obuf[eof]))
	X		return EOF;
	X	c = *op++;
	X	if (op >= oend)
	X		op = obuf;
	X	return (c & BYTEMASK);
	X}
	X
	X/*
	Xchar tr[] = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
	X	     0 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
	X	     0                1               2               3 
	Xtrlookup is used to translate by direct lookup.  The input character
	Xis an index into trlookup.  If the result is 0xFF, a bad char has been read.
	XAdded by:  Dan LaLiberte, liberte@uiucdcs.Uiuc.ARPA, ihnp4!uiucdcs!liberte
	X*/
	Xchar trlookup[83] = { 	0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
	X			0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0xFF, 0xFF,
	X			0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0xFF,
	X			0x14, 0x15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	X			0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
	X			0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0xFF,
	X			0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0xFF,
	X			0x2C, 0x2D, 0x2E, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF,
	X			0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF,
	X			0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0xFF, 0xFF,
	X			0x3D, 0x3E, 0x3F };
	X
	X/* q format -- decode one byte into 6 bit binary */
	Xget6bits()
	X{
	X	register int c;
	X	register int tc;
	X
	X	while (1) {
	X		c = getc(ifp);
	X		switch (c) {
	X		case '\n':
	X		case '\r':
	X			continue;
	X		case ':':
	X		case EOF:
	X			return EOF;
	X		default:
	X		 	tc = ((c-' ') < 83) ? trlookup[c-' '] : 0xff;
	X/*			fprintf(stderr, "c = '%c'  tc = %4x\n", c, tc); */
	X			if (tc != 0xff)
	X				return (tc);
	X			fprintf(stderr, "bad char: '%c'\n", c);
	X			return EOF;
	X		}
	X	}
	X}
	X
	X
	X#define CRCCONSTANT 0x1021
	X
	Xcomp_q_crc(c)
	Xregister unsigned int c;
	X{
	X	register int i;
	X	register unsigned int temp = crc;
	X
	X	for (i=0; i<8; i++) {
	X		c <<= 1;
	X		if ((temp <<= 1) & WORDBIT)
	X			temp = (temp & WORDMASK) ^ CRCCONSTANT;
	X		temp ^= (c >> 8);
	X		c &= BYTEMASK;
	X	}
	X	crc = temp;
	X}
	X
	X/* old format -- process .hex and .hcx files */
	Xdo_o_header(macname, filename)
	Xchar *macname, *filename;
	X{
	X	char namebuf[256];		/* big enough for both att & bsd */
	X	char ibuf[BUFSIZ];
	X	int n;
	X
	X	/* set up name for output files */
	X	if (macname[0] == '\0') {
	X		strcpy(namebuf, filename);
	X
	X		/* strip directories */
	X		macname = search_last(namebuf, '/');
	X		if (macname == NULL)
	X			macname = namebuf;
	X		else
	X			macname++;
	X
	X		/* strip extension */
	X		n = strlen(macname);
	X		if (n > 4) {
	X		    n -= 4;
	X		    if (macname[n] == '.' && macname[n+1] == 'h'
	X					    && macname[n+3] == 'x')
	X			    macname[n] = '\0';
	X		}
	X	}
	X	n = strlen(macname);
	X	if (n > NAMEBYTES)
	X		n = NAMEBYTES;
	X	strncpy(mh.m_name, macname, n);
	X	mh.m_name[n] = '\0';
	X
	X	/* read "#TYPEAUTH$flag"  line */
	X	if (fgets(ibuf, BUFSIZ, ifp) == NULL) {
	X		fprintf(stderr, "unexpected EOF\n");
	X		exit(2);
	X	}
	X	n = strlen(ibuf);
	X	if (n >= 7 && ibuf[0] == '#' && ibuf[n-6] == '$') {
	X		if (n >= 11)
	X			strncpy(mh.m_type, &ibuf[1], 4);
	X		if (n >= 15)
	X			strncpy(mh.m_author, &ibuf[5], 4);
	X		sscanf(&ibuf[n-5], "%4hx", &mh.m_flags);
	X	}
	X}
	X
	Xdo_o_forks()
	X{
	X	char ibuf[BUFSIZ];
	X	int forks = 0, found_crc = 0;
	X	unsigned int calc_crc, file_crc;
	X
	X
	X	crc = 0;	/* calculate a crc for both forks */
	X
	X	/* create empty files ahead of time */
	X	close(creat(files.f_data, 0666));
	X	close(creat(files.f_rsrc, 0666));
	X
	X	while (!found_crc && fgets(ibuf, BUFSIZ, ifp) != NULL) {
	X		if (forks == 0 && strncmp(ibuf, "***COMPRESSED", 13) == 0) {
	X			compressed++;
	X			continue;
	X		}
	X		if (strncmp(ibuf, "***DATA", 7) == 0) {
	X			mh.m_datalen = make_file(files.f_data, compressed);
	X			forks++;
	X			continue;
	X		}
	X		if (strncmp(ibuf, "***RESOURCE", 11) == 0) {
	X			mh.m_rsrclen = make_file(files.f_rsrc, compressed);
	X			forks++;
	X			continue;
	X		}
	X		if (compressed && strncmp(ibuf, "***CRC:", 7) == 0) {
	X			found_crc++;
	X			calc_crc = crc;
	X			sscanf(&ibuf[7], "%x", &file_crc);
	X			break;
	X		}
	X		if (!compressed && strncmp(ibuf, "***CHECKSUM:", 12) == 0) {
	X			found_crc++;
	X			calc_crc = crc & BYTEMASK;
	X			sscanf(&ibuf[12], "%x", &file_crc);
	X			file_crc &= BYTEMASK;
	X			break;
	X		}
	X	}
	X
	X	if (found_crc)
	X		verify_crc(calc_crc, file_crc);
	X	else {
	X		fprintf(stderr, "missing CRC\n");
	X		exit(3);
	X	}
	X}
	X
	Xmake_file(fname, compressed)
	Xchar *fname;
	Xint compressed;
	X{
	X	char ibuf[BUFSIZ];
	X	FILE *outf;
	X	int nbytes = 0;
	X
	X	outf = fopen(fname, "w");
	X	if (outf == NULL) {
	X		perror(fname);
	X		exit(-1);
	X	}
	X
	X	while (fgets(ibuf, BUFSIZ, ifp) != NULL) {
	X		if (strncmp(ibuf, "***END", 6) == 0)
	X			break;
	X		if (compressed)
	X			nbytes += comp_to_bin(ibuf, outf);
	X		else
	X			nbytes += hex_to_bin(ibuf, outf);
	X	}
	X
	X	fclose(outf);
	X	return nbytes;
	X}
	X
	Xcomp_c_crc(c)
	Xunsigned char c;
	X{
	X	crc = (crc + c) & WORDMASK;
	X	crc = ((crc << 3) & WORDMASK) | (crc >> 13);
	X}
	X
	Xcomp_e_crc(c)
	Xunsigned char c;
	X{
	X	crc += c;
	X}
	X
	X#define SIXB(c) (((c)-0x20) & 0x3f)
	X
	Xcomp_to_bin(ibuf, outf)
	Xchar ibuf[];
	XFILE *outf;
	X{
	X	char obuf[BUFSIZ];
	X	register char *ip = ibuf;
	X	register char *op = obuf;
	X	register int n, outcount;
	X	int numread, incount;
	X
	X	numread = strlen(ibuf);
	X	ip[numread-1] = ' ';		/* zap out the newline */
	X	outcount = (SIXB(ip[0]) << 2) | (SIXB(ip[1]) >> 4);
	X	incount = ((outcount / 3) + 1) * 4;
	X	for (n = numread; n < incount; n++)	/* restore lost spaces */
	X		ibuf[n] = ' ';
	X
	X	n = 0;
	X	while (n <= outcount) {
	X		*op++ = SIXB(ip[0]) << 2 | SIXB(ip[1]) >> 4;
	X		*op++ = SIXB(ip[1]) << 4 | SIXB(ip[2]) >> 2;
	X		*op++ = SIXB(ip[2]) << 6 | SIXB(ip[3]);
	X		ip += 4;
	X		n += 3;
	X	}
	X
	X	for (n=1; n <= outcount; n++)
	X		comp_c_crc((unsigned)obuf[n]);
	X
	X	fwrite(obuf+1, 1, outcount, outf);
	X	return outcount;
	X}
	X
	Xhex_to_bin(ibuf, outf)
	Xchar ibuf[];
	XFILE *outf;
	X{
	X	register char *ip = ibuf;
	X	register int n, outcount;
	X	int c;
	X
	X	n = strlen(ibuf) - 1;
	X	outcount = n / 2;
	X	for (n = 0; n < outcount; n++) {
	X		c = hexit(*ip++);
	X		comp_e_crc((unsigned)(c = (c << 4) | hexit(*ip++)));
	X		fputc(c, outf);
	X	}
	X	return outcount;
	X}
	X
	Xhexit(c)
	Xint c;
	X{
	X	if ('0' <= c && c <= '9')
	X		return c - '0';
	X	if ('A' <= c && c <= 'F')
	X		return c - 'A' + 10;
	X
	X	fprintf(stderr, "illegal hex digit: %c", c);
	X	exit(4);
	X}
	X
	Xput2(bp, value)
	Xchar *bp;
	Xshort value;
	X{
	X	*bp++ = (value >> 8) & BYTEMASK;
	X	*bp++ = value & BYTEMASK;
	X}
	X
	Xput4(bp, value)
	Xchar *bp;
	Xlong value;
	X{
	X	register int i, c;
	X
	X	for (i = 0; i < 4; i++) {
	X		c = (value >> 24) & BYTEMASK;
	X		value <<= 8;
	X		*bp++ = c;
	X	}
	X}
SHAR_EOF
if test 17305 -ne "`wc -c < 'xbin.c'`"
then
	echo shar: error transmitting "'xbin.c'" '(should have been 17305 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'xbin.l'" '(2338 characters)'
if test -f 'xbin.l'
then
	echo shar: will not over-write existing file "'xbin.l'"
else
sed 's/^	X//' << \SHAR_EOF > 'xbin.l'
	X.TH XBIN local "24 Mar 1985"
	X.UC 4
	X.SH NAME
	Xxbin \- convert mailable format BinHex file into binary before downloading
	Xto MacTerminal
	X.SH SYNOPSIS
	X.B xbin
	X[
	X.B \-o
	X]
	X[
	X.B \-v
	X]
	X[
	X.B \-l
	X]
	X[[
	X.B \-n
	Xname
	X] file] ...
	X.SH DESCRIPTION
	X.I Xbin
	Xconverts a file created by BinHex (usually
	Xnamed with one of the extensions ".hex", ".hcx", or ".hqx")
	Xinto three host-system files suitable for downloading to a
	XMacintosh via macput.
	XThis program is designed for use with the 1.1 Release
	Xversion of MacTerminal, but includes a compatibility option for the
	Xold -0.15X Almost-Alpha version.
	X.PP
	XThe
	X.B -l
	X(list) option reads the header information and
	Xprints out all the useful information there,
	Xwithout creating any converted output files.
	X.PP
	XThe
	X.B -v
	X(verbose) option prints a line for each file to be converted, indicating
	Xthe input and output file names.
	X.PP
	XThe
	X.B -n
	Xname
	Xoption allows the user to specify the name to use when creating
	Xthe host files and the eventual name to use on the mac.
	XThis option must precede the input file name it is to affect.
	X.PP
	XIf this option is not used, the names will be derived from
	Xeither the input file name (.hex or .hcx files),
	Xor the name encoded in the header information (.hqx files).
	XSpaces and slashes will be converted to underscores, and
	Xthe .h?x extension will be deleted, if one is included in the
	Xinput file name.
	X.PP
	XA file name of "-" indicates that the input should be taken from stdin.
	XIf no mac file name is specified, the default name (for .hex or .hcx files)
	Xis "stdin".
	X.PP
	XMail or news headers and signatures need not be manually
	Xstripped -- xbin will ignore pretty much anything
	Xit doesn't need.
	X.PP
	X.I xbin
	Xcreates three host-system files from each input file:
	X.IB name .info ,
	X.IB name .data ,
	Xand
	X.IB name .rsrc .
	X.PP
	XThe
	X.B \-o
	Xflag specifies "old" (version -0.15X) MacTerminal compatibility mode.
	X.SH BUGS
	XThe "LOCKED" bit in the flags cannot be set by xbin.
	XThis is due to a bug in MacTerminal, which sets the flags
	Xwhen the file is created, rather than after it has been
	Xtransfered, resulting in it not being able to write the
	Xfile.
	X.PP
	XInput files must contain a line starting with "(This file"
	Xto detect the beginning of the BinHex information.
	X.SH SEE ALSO
	Xmacput(1), macget(1)
	X.SH AUTHOR
	XDave Johnson, Brown 12/16/84;
	XCRC handling code by Darin Adler, TMQ Software 3/16/85
SHAR_EOF
if test 2338 -ne "`wc -c < 'xbin.l'`"
then
	echo shar: error transmitting "'xbin.l'" '(should have been 2338 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0