# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by crime!moriarty on Wed Apr 27 09:50:45 PDT 1988
# Contents:  makefile xbin.c xbin.p
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'
xbin: xbin.c
	/bin/rm -f xbin
	cc -O -o xbin -DBSD xbin.c

@//E*O*F makefile//
chmod u=rw,g=r,o=r makefile
 
echo x - xbin.c
sed 's/^@//' > "xbin.c" <<'@//E*O*F xbin.c//'
#ifndef lint
static char version[] = "@(#)xbin.c Version 2.5, April '88 (moriarty@tc.fluke.COM)";
#endif lint

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>

#ifdef MAXNAMLEN	/* 4.2 BSD */
#define FNAMELEN MAXNAMLEN
#else
#define FNAMELEN DIRSIZ
#endif

#ifdef BSD
#include <sys/time.h>
#include <sys/timeb.h>
#define search_last rindex
extern char *rindex();
#else
#include <time.h>
extern long timezone;
#define search_last strrchr
extern char *strrchr();
#endif

/* Mac time of 00:00:00 GMT, Jan 1, 1970 */
#define TIMEDIFF 0x7c25b080

#define DATABYTES 128

#define BYTEMASK 0xff
#define BYTEBIT 0x100
#define WORDMASK 0xffff
#define WORDBIT 0x10000

#define NAMEBYTES 63
#define H_NLENOFF 1
#define H_NAMEOFF 2

/* 65 <-> 80 is the FInfo structure */
#define H_TYPEOFF 65
#define H_AUTHOFF 69
#define H_FLAGOFF 73

#define H_LOCKOFF 81
#define H_DLENOFF 83
#define H_RLENOFF 87
#define H_CTIMOFF 91
#define H_MTIMOFF 95

#define H_OLD_DLENOFF 81
#define H_OLD_RLENOFF 85

#define F_BUNDLE 0x2000
#define F_LOCKED 0x8000

struct macheader {
	char m_name[NAMEBYTES+1];
	char m_type[4];
	char m_author[4];
	short m_flags;
	long m_datalen;
	long m_rsrclen;
	long m_createtime;
	long m_modifytime;
} mh;

struct filenames {
	char f_info[256];
	char f_data[256];
	char f_rsrc[256];
} files;

int pre_beta;	/* options */
int listmode;
int verbose;
int no_crc;

int compressed;	/* state variables */
int qformat;
FILE *ifp;

/*
 * xbin -- unpack BinHex format file into suitable
 * format for downloading with macput
 * Dave Johnson, Brown University Computer Science
 *
 * (c) 1984 Brown University
 * may be used but not sold without permission
 *
 * created ddj 12/16/84
 * revised ddj 03/10/85 -- version 4.0 compatibility, other minor mods
 * revised ddj 03/11/85 -- strip LOCKED bit from m_flags
 * revised ahm 03/12/85 -- System V compatibility
 * revised dba 03/16/85 -- (Darin Adler, TMQ Software)  4.0 EOF fixed,
 *			   4.0 checksum added
 * revised ddj 03/17/85 -- extend new features to older formats: -l, stdin
 * revised ddj 03/24/85 -- check for filename truncation, allow multiple files
 * revised ddj 03/26/85 -- fixed USG botches, many problems w/multiple files
 * revised jcb 03/30/85 -- (Jim Budler, amdcad!jimb), revised for compatibility
 *			   with 16-bit int machines
 * revised dl  06/16/85 -- (Dan LaLiberte, liberte@uiucdcs) character
 *			   translation speedup
 * revised ddj 09/30/85 -- fixed problem with run of RUNCHAR
 * revised gvr 11/15/86 -- speed-up: rewrote .hqx decoding (mcvax!guido)
 * revised jwm 4/26/88 -- now recognizes "(Convert with" as a starting line
 *                     -- the widely-used Stuffit uses this as a header
 */

char usage[] = "usage: \"xbin [-v] [-l] [-o] [-f] [-n name] [-] filename\"\n";

main(ac, av)
char **av;
{
	char *filename, *macname;

	filename = ""; macname = "";
	ac--; av++;
	while (ac) {
		if (av[0][0] == '-') {
			switch (av[0][1]) {
			case '\0':
				filename = "-";
				break;
			case 'v':
				verbose++;
				break;
			case 'l':
				listmode++;
				break;
			case 'o':
				pre_beta++;
				break;
			case 'f':
				no_crc++;
				break;
			case 'n':
				if (ac > 1) {
					ac--; av++;
					macname = av[0];
					filename = "";
					break;
				}
				else
					goto bad_usage;
			default:
				goto bad_usage;
			}
		}
		else
			filename = av[0];
		if (filename[0] != '\0') {
			setup_files(filename, macname);
			if (listmode) {
				print_header();
			}
			else {
				process_forks();
				/* now that we know the size of the forks */
				forge_info();
			}
			if (ifp != stdin)
				fclose(ifp);
			macname = "";
			ifp = NULL;		/* reset state */
			qformat = 0;
			compressed = 0;
		}
		ac--; av++;
	}
	if (*filename == '\0') {
bad_usage:
		fprintf(stderr, usage);
		exit(1);
	}
}

static char *extensions[] = {
	".hqx",
	".hcx",
	".hex",
	"",
	NULL
};

setup_files(filename, macname)
char *filename;		/* input file name -- extension optional */
char *macname;		/* name to use on the mac side of things */
{
	char namebuf[256];
	char **ep;
	struct stat stbuf;
	long curtime;

	if (filename[0] == '-') {
		ifp = stdin;
		filename = "stdin";
	}
	else {
		/* find input file and open it */
		for (ep = extensions; *ep != NULL; ep++) {
			sprintf(namebuf, "%s%s", filename, *ep);
			if (stat(namebuf, &stbuf) == 0)
				break;
		}
		if (*ep == NULL) {
			perror(namebuf);
			exit(-1);
		}
		ifp = fopen(namebuf, "r");
		if (ifp == NULL) {
			perror(namebuf);
			exit(-1);
		}
	}
	if (ifp == stdin) {
		curtime = time(0);
		mh.m_createtime = curtime;
		mh.m_modifytime = curtime;
	}
	else {
		mh.m_createtime = stbuf.st_mtime;
		mh.m_modifytime = stbuf.st_mtime;
	}
	if (listmode || verbose) {
		fprintf(stderr, "%s %s%s",
			listmode ? "\nListing" : "Converting",
			namebuf, listmode ? ":\n" : " ");
	}

	qformat = find_header(); /* eat mailer header &cetera, intuit format */

	if (qformat)
		do_q_everything(macname, namebuf);
	else {
		do_o_header(macname, filename);
		name_stuffing(macname);
	}
}

name_stuffing(namebuf)
char *namebuf;
{
	int n;
	char *np;
	/* make sure host file name doesn't get truncated beyond recognition */
	n = strlen(mh.m_name);
	if (n > FNAMELEN - 2)
		n = FNAMELEN - 2;
	strncpy(namebuf, mh.m_name, n);
	namebuf[n] = '\0';

        /* get rid of troublesome characters */
/* I replaced the following code to avoid a core-dump when the file to */
/* be created by xbin contains characters which could not be handled  */
/* Werner Uhrig, June 6, 1988.  <werner@rascal.ics.utexas.edu>        */
/*      I give credit to Earle Horton, who posted the C-code fraction */
/*	I used as replacement. */

/* 	for (np = namebuf; *np; np++)
		if (*np == ' ' || *np == '/')
			*np = '_';
*/
        for (np = namebuf; *np; np++){
                if (*np == ' ' || *np == '/' || *np == '!' ||
                *np == '(' || *np == ')' || *np == '[' || *np == ']'
                || *np == '*' || *np == '<' || *np == '>' ||
                *np == '?' || *np == '\'' || *np == '"' || *np == '$')
                        *np = '_';
                *np &= 0x7f;
        }
        sprintf(files.f_data, "%s.data", namebuf);
        sprintf(files.f_rsrc, "%s.rsrc", namebuf);
        sprintf(files.f_info, "%s.info", namebuf);
	if (verbose)
		fprintf(stderr, "==> %s.{info,data,rsrc}\n", namebuf);
}

/* print out header information in human-readable format */
print_header()
{
	char *ctime();

	printf("macname: %s\n", mh.m_name);
	printf("filetype: %.4s, ", mh.m_type);
	printf("author: %.4s, ", mh.m_author);
	printf("flags: 0x%x\n", mh.m_flags);
	if (qformat) {
		printf("data length: %ld, ", mh.m_datalen);
		printf("rsrc length: %ld\n", mh.m_rsrclen);
	}
	if (!pre_beta) {
		printf("create time: %s", ctime(&mh.m_createtime));
	}
}

process_forks()
{
	if (qformat) {
		/* Forks have already been read. */
	}
	else
		do_o_forks();
}

/* write out .info file from information in the mh structure */
forge_info()
{
	static char buf[DATABYTES];
	char *np;
	FILE *fp;
	int n;
	long tdiff;
	struct tm *tp;
#ifdef BSD
	struct timeb tbuf;
#else
	long bs;
#endif

	for (np = mh.m_name; *np; np++)
		if (*np == '_') *np = ' ';

	buf[H_NLENOFF] = n = np - mh.m_name;
	strncpy(buf + H_NAMEOFF, mh.m_name, n);
	strncpy(buf + H_TYPEOFF, mh.m_type, 4);
	strncpy(buf + H_AUTHOFF, mh.m_author, 4);
	put2(buf + H_FLAGOFF, mh.m_flags & ~F_LOCKED);
	if (pre_beta) {
		put4(buf + H_OLD_DLENOFF, mh.m_datalen);
		put4(buf + H_OLD_RLENOFF, mh.m_rsrclen);
	}
	else {
		put4(buf + H_DLENOFF, mh.m_datalen);
		put4(buf + H_RLENOFF, mh.m_rsrclen);

		/* convert unix file time to mac time format */
#ifdef BSD
		ftime(&tbuf);
		tp = localtime(&tbuf.time);
		tdiff = TIMEDIFF - tbuf.timezone * 60;
		if (tp->tm_isdst)
			tdiff += 60 * 60;
#else
		/* I hope this is right! -andy */
		time(&bs);
		tp = localtime(&bs);
		tdiff = TIMEDIFF - timezone;
		if (tp->tm_isdst)
			tdiff += 60 * 60;
#endif
		put4(buf + H_CTIMOFF, mh.m_createtime + tdiff);
		put4(buf + H_MTIMOFF, mh.m_modifytime + tdiff);
	}
	fp = fopen(files.f_info, "w");
	if (fp == NULL) {
		perror("info file");
		exit(-1);
	}
	fwrite(buf, 1, DATABYTES, fp);
	fclose(fp);
}

/* eat characters until header detected, return which format */
find_header()
{
	int c, at_bol;
	char ibuf[BUFSIZ];

	/* look for "(This file ...)" of "(Convert with...)"line */
	while (fgets(ibuf, BUFSIZ, ifp) != NULL) {
		if ((strncmp(ibuf, "(This file", 10) == 0) ||
		    (strncmp(ibuf, "(Convert with", 13) == 0))
			break;
	}
	at_bol = 1;
	while ((c = getc(ifp)) != EOF) {
		switch (c) {
		case '\n':
		case '\r':
			at_bol = 1;
			break;
		case ':':
			if (at_bol)	/* q format */
				return 1;
			break;
		case '#':
			if (at_bol) {	/* old format */
				ungetc(c, ifp);
				return 0;
			}
			break;
		default:
			at_bol = 0;
			break;
		}
	}

	fprintf(stderr, "unexpected EOF\n");
	exit(2);
	/* NOTREACHED */
}

static unsigned int crc;

short get2q();
long get4q();

/* read header of .hqx file */
do_q_header(macname)
char *macname;
{
	char namebuf[256];		/* big enough for both att & bsd */
	int n;
	unsigned int calc_crc, file_crc;

	crc = 0;			/* compute a crc for the header */
	/* q_init();			/* reset static variables */

	n = getq();			/* namelength */
	n++;				/* must read trailing null also */
	getqbuf(namebuf, n);		/* read name */
	if (macname[0] == '\0')
		macname = namebuf;

	n = strlen(macname);
	if (n > NAMEBYTES)
		n = NAMEBYTES;
	strncpy(mh.m_name, macname, n);
	mh.m_name[n] = '\0';

	getqbuf(mh.m_type, 4);
	getqbuf(mh.m_author, 4);
	mh.m_flags = get2q();
	mh.m_datalen = get4q();
	mh.m_rsrclen = get4q();

	comp_q_crc(0);
	comp_q_crc(0);
	calc_crc = crc;
	file_crc = get2q();
	verify_crc(calc_crc, file_crc);
}

/* verify_crc(); -- check if crc's check out */
verify_crc(calc_crc, file_crc)
unsigned int calc_crc, file_crc;
{
	calc_crc &= WORDMASK;
	file_crc &= WORDMASK;

	if (calc_crc != file_crc) {
		fprintf(stderr, "CRC error\n---------\n");
		fprintf(stderr, "CRC in file:\t0x%x\n", file_crc);
		fprintf(stderr, "calculated CRC:\t0x%x\n", calc_crc);
		exit(3);
	}
}

/* New stuff which hopes to improve the speed. */

#define RUNCHAR 0x90

#define DONE 0x7F
#define SKIP 0x7E
#define FAIL 0x7D

char lookup[256] = {
/* 0*/	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/*	            \n                \r		*/
	FAIL, FAIL, SKIP, FAIL, FAIL, SKIP, FAIL, FAIL,
/* 2*/	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 4*/	FAIL, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
	0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, FAIL, FAIL,
/* 6*/	0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, FAIL,
/*	            :					*/
	0x14, 0x15, DONE, FAIL, FAIL, FAIL, FAIL, FAIL,
/* 8*/	0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
	0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, FAIL,
/*10*/	0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, FAIL,
	0x2C, 0x2D, 0x2E, 0x2F, FAIL, FAIL, FAIL, FAIL,
/*12*/	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, FAIL,
	0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, FAIL, FAIL,
/*14*/	0x3D, 0x3E, 0x3F, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
/*16*/	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
	FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL,
};

char *g_macname;
char *g_namebuf;

int stop= 0;

get_header()
{
	do_q_header(g_macname);
	name_stuffing(g_namebuf);
	if (listmode) {
		stop= 1;
		return;
	}
}

unsigned char obuf[BUFSIZ];
unsigned char *op= obuf;
unsigned char *oq;

#define S_HEADER	0
#define S_DATAOPEN	1
#define S_DATAWRITE	2
#define S_DATAC1	3
#define S_DATAC2	4
#define S_RSRCOPEN	5
#define S_RSRCWRITE	6
#define S_RSRCC1	7
#define S_RSRCC2	8
#define S_EXCESS	9

int ostate= S_HEADER;

unsigned int calc_crc;
unsigned int file_crc;

long todo;
FILE *ofp;

oflush()
{
	int n;
	
	oq= obuf;
	while (oq < op && !stop) {
		switch (ostate) {
		case S_HEADER:
			get_header();
			++ostate;
			break;
		case S_DATAOPEN:
			ofp= fopen(files.f_data, "w");
			todo= mh.m_datalen;
			crc= 0;
			++ostate;
			break;
		case S_RSRCOPEN:
			ofp= fopen(files.f_rsrc, "w");
			todo= mh.m_rsrclen;
			crc= 0;
			++ostate;
			break;
		case S_DATAWRITE:
		case S_RSRCWRITE:
			n= op-oq;
			if (n > todo)
				n= todo;
			if (fwrite(oq, 1, n, ofp) != n) {
				perror("fwrite");
				exit(1);
			}
			if (!no_crc) {
				register unsigned char *end= oq+n;
				register unsigned char *p= oq;
				while (p < end)
					comp_q_crc(*p++);
			}
			oq += n;
			todo -= n;
			if (todo <= 0) {
				fclose(ofp);
				++ostate;
			}
			break;
		case S_DATAC1:
		case S_RSRCC1:
			comp_q_crc(0);
			comp_q_crc(0);
			calc_crc= crc;
			file_crc= getq() << 8;
			++ostate;
			break;
		case S_DATAC2:
		case S_RSRCC2:
			/* Skip crc bytes */
			file_crc |= getq();
			if (!no_crc)
				verify_crc(calc_crc, file_crc);
			++ostate;
			break;
		case S_EXCESS:
			fprintf(stderr, "%d excess bytes ignored\n", op-oq);
			oq= op;
			break;
		}
	}
	op= obuf;
}

getq()
{
	int c;
	
	if (oq >= op) {
		fprintf(stderr, "premature EOF\n");
		exit(1);
	}
	c= *oq++;
	comp_q_crc((unsigned)c);
	return c;
}

/* get2q(); q format -- read 2 bytes from input, return short */
short
get2q()
{
	short high= getq() << 8;
	return high | getq();
}

/* get4q(); q format -- read 4 bytes from input, return long */
long
get4q()
{
	int i;
	long value= 0;

	for (i= 0; i < 4; i++)
		value= (value<<8) | getq();
	return value;
}

/* getqbuf(); q format -- read n characters from input into buf */
getqbuf(buf, n)
	char *buf;
	int n;
{
	int i;

	for (i = 0; i < n; i++)
		*buf++ = getq();
}

#define output(c) { *op++ = (c); if (op >= &obuf[BUFSIZ]) oflush(); }

do_q_everything(macname, namebuf)
char *macname;
char *namebuf;
{
	unsigned char buf[BUFSIZ];
	int n;
	register unsigned char *in, *out;
	register int b6, b8, data, lastc= 0;
	char state68= 0, run= 0;
	
	g_macname= macname;
	g_namebuf= namebuf;
	
	ostate= S_HEADER;
	stop= 0;
	
	while (!stop && (n= fread(buf, 1, BUFSIZ, ifp)) > 0) {
		in= buf;
		out= buf+n;
		do {
			if ((b6= lookup[*in]) >= 64) {
				switch (b6) {
				case DONE:
					goto done;
				case SKIP:
					break;
				default:
					fprintf(stderr,
						"bad char '%c'(%d)\n",
						*in, *in);
					goto done;
				}
			}
			else {
				/* Pack 6 bits to 8 bits */
				switch (state68++) {
				case 0:
					b8= b6<<2;
					continue; /* No data byte */
				case 1:
					data= b8 | (b6>>4);
					b8= (b6&0xF) << 4;
					break;
				case 2:
					data= b8 | (b6>>2);
					b8= (b6&0x3) << 6;
					break;
				case 3:
					data= b8 | b6;
					state68= 0;
					break;
				}
				if (!run) {
					if (data == RUNCHAR)
						run= 1;
					else
						output(lastc= data);
				}
				else {
					if (data == 0) {
						output(lastc= RUNCHAR);
					}
					else {
						while (--data > 0) {
							output(lastc);
						}
					}
					run= 0;
				}
			}
		} while (++in < out);
	}
 done:	oflush();
 	if (!stop && ostate != S_EXCESS) {
 		fprintf(stderr, "premature EOF\n");
 		exit(1);
 	}
}

/* Ahh, too bad.  The following routine takes up half of the CPU time.
   But in this form it's far to complicated to turn it into a macro! */

#define CRCCONSTANT 0x1021

comp_q_crc(c)
register unsigned int c;
{
	register int i;
	register unsigned long temp = crc;

/* Never mind why I call it WOP... */
#define WOP { \
		c <<= 1; \
		if ((temp <<= 1) & WORDBIT) \
			temp = (temp & WORDMASK) ^ CRCCONSTANT; \
		temp ^= (c >> 8); \
		c &= BYTEMASK; \
	}
	WOP;
	WOP;
	WOP;
	WOP;
	WOP;
	WOP;
	WOP;
	WOP;
	crc = temp;
}

/* old format -- process .hex and .hcx files */
do_o_header(macname, filename)
char *macname, *filename;
{
	char namebuf[256];		/* big enough for both att & bsd */
	char ibuf[BUFSIZ];
	int n;

	/* set up name for output files */
	if (macname[0] == '\0') {
		strcpy(namebuf, filename);

		/* strip directories */
		macname = search_last(namebuf, '/');
		if (macname == NULL)
			macname = namebuf;
		else
			macname++;

		/* strip extension */
		n = strlen(macname);
		if (n > 4) {
		    n -= 4;
		    if (macname[n] == '.' && macname[n+1] == 'h'
					    && macname[n+3] == 'x')
			    macname[n] = '\0';
		}
	}
	n = strlen(macname);
	if (n > NAMEBYTES)
		n = NAMEBYTES;
	strncpy(mh.m_name, macname, n);
	mh.m_name[n] = '\0';

	/* read "#TYPEAUTH$flag"  line */
	if (fgets(ibuf, BUFSIZ, ifp) == NULL) {
		fprintf(stderr, "unexpected EOF\n");
		exit(2);
	}
	n = strlen(ibuf);
	if (n >= 7 && ibuf[0] == '#' && ibuf[n-6] == '$') {
		if (n >= 11)
			strncpy(mh.m_type, &ibuf[1], 4);
		if (n >= 15)
			strncpy(mh.m_author, &ibuf[5], 4);
		sscanf(&ibuf[n-5], "%4hx", &mh.m_flags);
	}
}

do_o_forks()
{
	char ibuf[BUFSIZ];
	int forks = 0, found_crc = 0;
	unsigned int calc_crc, file_crc;
	extern long make_file();


	crc = 0;	/* calculate a crc for both forks */

	/* create empty files ahead of time */
	close(creat(files.f_data, 0666));
	close(creat(files.f_rsrc, 0666));

	while (!found_crc && fgets(ibuf, BUFSIZ, ifp) != NULL) {
		if (forks == 0 && strncmp(ibuf, "***COMPRESSED", 13) == 0) {
			compressed++;
			continue;
		}
		if (strncmp(ibuf, "***DATA", 7) == 0) {
			mh.m_datalen = make_file(files.f_data, compressed);
			forks++;
			continue;
		}
		if (strncmp(ibuf, "***RESOURCE", 11) == 0) {
			mh.m_rsrclen = make_file(files.f_rsrc, compressed);
			forks++;
			continue;
		}
		if (compressed && strncmp(ibuf, "***CRC:", 7) == 0) {
			found_crc++;
			calc_crc = crc;
			sscanf(&ibuf[7], "%x", &file_crc);
			break;
		}
		if (!compressed && strncmp(ibuf, "***CHECKSUM:", 12) == 0) {
			found_crc++;
			calc_crc = crc & BYTEMASK;
			sscanf(&ibuf[12], "%x", &file_crc);
			file_crc &= BYTEMASK;
			break;
		}
	}

	if (found_crc)
		verify_crc(calc_crc, file_crc);
	else {
		fprintf(stderr, "missing CRC\n");
		exit(3);
	}
}

long
make_file(fname, compressed)
char *fname;
int compressed;
{
	char ibuf[BUFSIZ];
	FILE *outf;
	register long nbytes = 0L;

	outf = fopen(fname, "w");
	if (outf == NULL) {
		perror(fname);
		exit(-1);
	}

	while (fgets(ibuf, BUFSIZ, ifp) != NULL) {
		if (strncmp(ibuf, "***END", 6) == 0)
			break;
		if (compressed)
			nbytes += comp_to_bin(ibuf, outf);
		else
			nbytes += hex_to_bin(ibuf, outf);
	}

	fclose(outf);
	return nbytes;
}

comp_c_crc(c)
unsigned char c;
{
	crc = (crc + c) & WORDMASK;
	crc = ((crc << 3) & WORDMASK) | (crc >> 13);
}

comp_e_crc(c)
unsigned char c;
{
	crc += c;
}

#define SIXB(c) (((c)-0x20) & 0x3f)

comp_to_bin(ibuf, outf)
char ibuf[];
FILE *outf;
{
	char obuf[BUFSIZ];
	register char *ip = ibuf;
	register char *op = obuf;
	register int n, outcount;
	int numread, incount;

	numread = strlen(ibuf);
	ip[numread-1] = ' ';		/* zap out the newline */
	outcount = (SIXB(ip[0]) << 2) | (SIXB(ip[1]) >> 4);
	incount = ((outcount / 3) + 1) * 4;
	for (n = numread; n < incount; n++)	/* restore lost spaces */
		ibuf[n] = ' ';

	n = 0;
	while (n <= outcount) {
		*op++ = SIXB(ip[0]) << 2 | SIXB(ip[1]) >> 4;
		*op++ = SIXB(ip[1]) << 4 | SIXB(ip[2]) >> 2;
		*op++ = SIXB(ip[2]) << 6 | SIXB(ip[3]);
		ip += 4;
		n += 3;
	}

	for (n=1; n <= outcount; n++)
		comp_c_crc((unsigned)obuf[n]);

	fwrite(obuf+1, 1, outcount, outf);
	return outcount;
}

hex_to_bin(ibuf, outf)
char ibuf[];
FILE *outf;
{
	register char *ip = ibuf;
	register int n, outcount;
	int c;

	n = strlen(ibuf) - 1;
	outcount = n / 2;
	for (n = 0; n < outcount; n++) {
		c = hexit(*ip++);
		comp_e_crc((unsigned)(c = (c << 4) | hexit(*ip++)));
		fputc(c, outf);
	}
	return outcount;
}

hexit(c)
int c;
{
	if ('0' <= c && c <= '9')
		return c - '0';
	if ('A' <= c && c <= 'F')
		return c - 'A' + 10;

	fprintf(stderr, "illegal hex digit: %c", c);
	exit(4);
	/* NOTREACHED */
}

put2(bp, value)
char *bp;
short value;
{
	*bp++ = (value >> 8) & BYTEMASK;
	*bp++ = value & BYTEMASK;
}

put4(bp, value)
char *bp;
long value;
{
	register int i, c;

	for (i = 0; i < 4; i++) {
		c = (value >> 24) & BYTEMASK;
		value <<= 8;
		*bp++ = c;
	}
}
@//E*O*F xbin.c//
chmod u=r,g=r,o=r xbin.c
 
echo x - xbin.p
sed 's/^@//' > "xbin.p" <<'@//E*O*F xbin.p//'
@.TH XBIN public "26 April 1988"
@.UC 4
@.SH NAME
xbin \- convert mailable format BinHex file into binary before downloading
to MacTerminal
@.SH SYNOPSIS
@.B xbin
[
@.B \-o
]
[
@.B \-v
]
[
@.B \-l
]
[
@.B \-f
]
[[
@.B \-n
name
] file] ...
@.SH DESCRIPTION
@.I Xbin
converts a file created by BinHex (usually
named with one of the extensions ".hex", ".hcx", or ".hqx")
into three host-system files suitable for downloading to a
Macintosh via macput.
This program is designed for use with the 1.1 Release
version of MacTerminal, but includes a compatibility option for the
old -0.15X Almost-Alpha version.
@.PP
The
@.B -l
(list) option reads the header information and
prints out all the useful information there,
without creating any converted output files.
@.PP
The
@.B -v
(verbose) option prints a line for each file to be converted, indicating
the input and output file names.
@.PP
The
@.B -f
(fast) option skips the CRC calculation; this is about five times
as fast as normal operation, but transmission errors may go undetected.
@.PP
The
@.B -n
name
option allows the user to specify the name to use when creating
the host files and the eventual name to use on the mac.
This option must precede the input file name it is to affect.
@.PP
If this option is not used, the names will be derived from
either the input file name (.hex or .hcx files),
or the name encoded in the header information (.hqx files).
Spaces and slashes will be converted to underscores, and
the .h?x extension will be deleted, if one is included in the
input file name.
@.PP
A file name of "-" indicates that the input should be taken from stdin.
If no mac file name is specified, the default name (for .hex or .hcx files)
is "stdin".
@.PP
Mail or news headers and signatures need not be manually
stripped -- xbin will ignore pretty much anything
it doesn't need.
@.PP
@.I Xbin
creates three host-system files from each input file:
@.IB name .info ,
@.IB name .data ,
and
@.IB name .rsrc .
@.PP
The
@.B \-o
flag specifies "old" (version -0.15X) MacTerminal compatibility mode.
@.SH BUGS
The "LOCKED" bit in the flags cannot be set by xbin.
This is due to a bug in MacTerminal, which sets the flags
when the file is created, rather than after it has been
transfered, resulting in it not being able to write the
file.
@.PP
Input files must contain a line starting with "(This file" or "(Convert with"
(used by StuffIt) to detect the beginning of the BinHex information.
@.SH SEE ALSO
macput(1), macget(1)
@.SH AUTHOR
Dave Johnson, Brown 12/16/84;
CRC handling code by Darin Adler, TMQ Software 3/16/85;
StuffIt compatibility by Jeff Meyer, John Fluke Company 4/26/88.
@//E*O*F xbin.p//
chmod u=rw,g=r,o= xbin.p
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
       4      11      59 makefile
     983    2965   19717 xbin.c
      96     467    2607 xbin.p
    1083    3443   22383 total
!!!
wc  makefile xbin.c xbin.p | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0

