[net.sources.mac] New SUMacC rmaker.shar

brian@ut-sally.UUCP (Brian H. Powell) (07/08/85)

# 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 ut-sally!brian on Sun Jul  7 00:06:04 CDT 1985
# Contents:  README rmaker.c rmaker.1 rmaker.doc crtdrvr.s crtproc.s
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
     As some of you have noticed, there is more than one version of the
SUMacC rmaker out there.  This is an attempt to combine all versions.


Summary of changes to rmaker:

Work done by Croft:
	fix fwrite bug and add INIT and PACK resource types.
Work done by Maio/Schilit:
	no NULL in DRVR name if device driver.
Work done by Moy:
	Implement CDEF, MDEF, WDEF and modify DRVR for dynamic relocation.
Work done by van Rossum:
	Added STR# resource types.
Work done by Crosswell:
	Added POST (PostScript) resource type.
Work done by Horvath:
	Added backslash escape sequences for strings.

Work done by Powell:
	Combine all of above work.
	Modify INIT and PACK to use dynamic relocation.
	Added FKEY and PROC resource types.
	Fix minor bugs in backslash code.

_______________________________________________________________________________

     The dynamic relocation work done by Moy requires the use of special
crt*.s files for DRVR, PROC, CDEF, MDEF, WDEF, INIT, PACK and FKEY types.
For DRVR's, use crtdrvr.s (which is included in the shar file) as an example.
For the others, use crtproc.s (also included) as an example.
@//E*O*F README//
chmod u=r,g=r,o=r README
 
echo x - rmaker.c
sed 's/^@//' > "rmaker.c" <<'@//E*O*F rmaker.c//'
/*	rmaker.c	1.0	04/11/84	*/

/*
 * Resource compiler.
 */

/* 
 * Copyright (C) 1984, Stanford Univ. SUMEX project.  
 * May be used but not sold without permission.
 */

/*
 * history
 * 04/11/84	Croft		Created.
 * 04/16/84	Croft		Reloc info now imbedded in long addresses.
 * 06/18/84	Schilit 	Added DITL and ALRT.
 * 07/06/84	Croft		Added DLOG WIND MENU CNTL ICON ICN# CURS PAT.
 * 07/21/84	Croft		Added DRVR; multiple code segs (jks).
 * 07/26/84	Schuster 	Added resource names.
 *** Branch 1
 * 01/10/85	Moy		Implement [CMW]DEF.  These and DRVR use dynamic
 *				relocation scheme.
 *** Branch 2
 * 09/18/84	Croft		Fixed fwrite bug for resource names.
 * 11/06/84	Croft		Added INIT and PACK.
 * 11/11/84   	Maio/Schilit	no NULL in DRVR name if device driver.
 *** Branch 3
 * 06/25/85	GvR		Added STR#.
 * 06/26/85	Crosswell	Added POST (PostScript resource in
 *				Laser Prep file)
 * 07/06/85	Horvath		Add Backslash escapes in strings.
 *** Reunion
 * 06/27/85	Powell		Combined branches 1, 2 and 3.
 * 06/27/85	Powell		Modify INIT and PACK to use dynamic relocation.
 * 06/27/85	Powell		Added FKEY.
 * 06/29/85	Powell		Added PROC.
 * 07/06/85	Powell		Fix tolower bug in backslash code.
				Limit \ooo to three digits, \xhh to two.
 */

#include <stdio.h>
#include <ctype.h>
#include <res.h>
#include <b.out.h>
#include <quickdraw.h>
#include <toolintf.h>

#define	bh	filhdr

struct	bhdr	bh;		/* b.out header */
char 	*malloc();
char	*index();
char	*rindex();
unsigned short	htons();	/* host to "net" byte order, short. */
unsigned long	htonl();

char	seg0[sizeof(struct jumphead) + sizeof(struct jumptab)] = {
	0,0,0,0x28,  0,0,2,0,  0,0,0,8,  0,0,0,0x20,
	0,0,  0x3F,0x3C,  0,1,  0xA9,0xF0
};				/* "standard" segment 0 jump header/table */
char	seg1[sizeof(struct codehead)] = { 0,0,  0,1 };

#define	CRTMAGIC 0x602C		/* jump at beginning of crtmac.s */
#define	CRTLEN	10		/* length of relocation table in crtmac.s */
#define	RELMAGIC 0x6034		/* jump at beginning of crt*.s */
#define	RELLEN	12		/* length of relocation table in crt*.s */

#define	NRESCOMP 50		/* max number of resources per compile */

struct	rescomp {		/* resource being compiled */
	char	rc_type[8];	/* resource type (e.g. "CODE") */
 	char	*rc_name;	/* resource name */
	int	rc_id;		/* resource id number */
	int	rc_att;		/* attributes */
	int	rc_length;	/* length in resource file */
	char	*rc_data;	/* pointer to data */
	int	rc_datalen;	/* length of data */
	FILE	*rc_file;	/* file to read data from */
	int	rc_filelen;	/* length of data in file */
	int	rc_bss;		/* number of zero pad bytes */
	char	*rc_rel;	/* relocation info */
	int	rc_rellen;	/* length of relocation info */
} rescomp[NRESCOMP], *rcp;

struct	resfile	rf;		/* compiled resource file header */
struct	resmap	rm;		/* compiled resource map header */
struct	restype	rt[NRESCOMP];	/* compiled resource type list */
struct	resid	ri[NRESCOMP];	/* compiled resource id list */

#define NAMELEN 1024
char 	rn[NAMELEN];		/* compiled resource name list */

char	debugtype[8];		/* debug type switch */
FILE	*fout;			/* file for compiler output */
FILE	*fin;			/* file for compiler commands */
char	fineof;			/* true after all commands read */
char	line[256];		/* line buffer */
char	*lp;			/* current position in line */
int	linenum;		/* line number in command file */

char	*relpnt;		/* current position in longrun area */
char	*relrel;		/* current position in reloc area */
int	rellen;			/* length of longrun area */
int	reloff;			/* current relocation offset */

char	token[128];		/* current token being parsed */
char	data[8*1024];		/* area to build simple resource data */
char	*datap;			/* pointer to data area */

/* type format handlers */
extern	handstr(), handhexa(), handcode(), handdrvr(), handproc();
extern	handdlog(), handalrt(), handditl();
extern	handwind(), handmenu(), handcntl(), handinit();
extern	handstrl(), handpost();

struct	typehand {		/* type string to handler table */
	char	th_type[8];	/* e.g. "CODE" */
	int	(*th_handler)(); /* format handler function */
} typehand[] = {
	"STR ",	handstr,
	"STR#", handstrl,
	"HEXA",	handhexa,
	"CODE",	handcode,
	"DRVR",	handdrvr,
	"INIT", handproc,
	"PACK", handproc,
	"ALRT", handalrt,
	"DITL", handditl, 
	"DLOG", handdlog,
	"WIND", handwind,
	"MENU", handmenu,
	"CNTL", handcntl,
	"ICON", handhexa,
	"ICN#", handhexa,
	"CURS", handhexa,
	"PAT ", handhexa,
	"CDEF",	handproc,
	"MDEF",	handproc,
	"WDEF",	handproc,
	"POST", handpost,
	"FKEY", handproc,
	"PROC", handproc,
	0,	0
};


main(argc, argv)
	char **argv;
{
	for (argc--,argv++ ; argc > 0 ; argc--,argv++) {
		if (argv[0][0] != '-')
			break;
		switch (argv[0][1]) {
		case 'd':
			argc--,argv++;
			if (argc < 1) abort("syntax: -d TYPE");
			strcpy(debugtype,argv[0]);
			break;
		}
	}
	if (argc != 1)
		abort("usage: rmaker commandfilename");
	if ((fin = fopen(argv[0], "r")) == NULL)
		abort("can't open commandfile");
	rmaker();
	buildrf();	/* build resource file from rescomp */
	exit(0);
}


rmaker()
{
	register i;
 	char infile[32], *ip;
	struct typehand *thp;
	int haveoutfile = 0;
	int items, id, att;
	char littype[32], type[32], format[32];

	rcp = &rescomp[0];
	while (fineof == 0) {
		if (getline() == 0)
			continue;	/* skip blank lines */
		if (haveoutfile == 0) {	/* if output file not yet open */
			if ((fout = fopen(lp, "w")) == NULL)
				abort("can't open output file %s", lp);
			haveoutfile++;
			continue;
		}
		littype[0] = type[0] = 0;
		items = sscanf(lp, "%s %s = %s", littype, type, format);
		if (items < 2 || strcmp(littype, "Type") != 0)
			abort("bad Type line");
		checktype(type);
		strcpy(rcp->rc_type, type);
		if (items == 3) {
			checktype(format);
			strcpy(type, format);
		}
		if (getline() == 0)
			abort("bad id");
		if (skipsp() == 0)
			abort("bad id");
		for (i=0 ; *lp != ',' && *lp != 0 ; lp++,i++)
			infile[i] = *lp;
		infile[i] = 0;
		if (*lp != ',')
			abort("bad id");
		lp++;
		id = att = 0;
		items = sscanf(lp, " %d(%d) ", &id, &att);
 		ip = index(infile, '|');
 		if (ip) {
 			*ip++ = 0;
 			if ((rcp->rc_name = malloc(strlen(ip) + 1)) == 0)
 				abort("name malloc");
 			strcpy(rcp->rc_name, ip);
 		} else
 			rcp->rc_name = 0;
		if (items < 1)
			abort("bad id");
		if (strlen(infile)) {
			if ((rcp->rc_file = fopen(infile, "r")) == NULL)
				abort("can't open input file %s", infile);
		} else {
			rcp->rc_file = 0;
		}
		rcp->rc_id = id;
		rcp->rc_att = att;
		/* search for type handler */
		for (thp = &typehand[0] ; ; thp++) {
			if (thp->th_handler == 0)
				abort("type %s not implemented", type);
			if (strcmp(thp->th_type, type) == 0)
				break;
		}
		datap = data;
		(*thp->th_handler)();
		if (datap != data) {
			int len = datap - data;
			if (len & 1) {
				len++;
				*datap++ = 0;
			}
			if ((rcp->rc_data = malloc(len)) == 0)
				abort("data malloc");
			bcopy(data, rcp->rc_data, len);
			rcp->rc_datalen = rcp->rc_length = len;
		}
		if (strcmp(type, debugtype) == 0)
			printrcp();
		rcp++;
		if ((rcp - &rescomp[0]) > NRESCOMP - 3)
			abort("too many resources");
	}
	if (rcp == &rescomp[0] || fout == 0)
		abort("nothing to do");
}


/*
 * Build resource file output from incore rescomp structure.
 */
buildrf()
{
	register struct rescomp *rcpp;
	register struct resid *rip;
	register struct restype *rtp;
 	register char *rnp;
	struct restype *rtpp;
	int offdata, roundtomap, sizetypes, sizemap;
	register i;
	register char *cp;
	numtypes_t numtypes;
	lendata_t lendata;

	/* XXX TODO: before scanning, sort rescomp by type/id */
	rtp = &rt[0];
	rip = &ri[0];
 	rnp = rn;
	rcpp = &rescomp[0];
	offdata = 0;
	/*
	 * Scan through the resources making type and id lists.  In this
	 * 1st pass, the rt_offids field is set to the offset from the
	 * start of the id's list.  Below, the 2nd pass adds the size
	 * of the type list to this field.
	 */
	bcopy(rcpp->rc_type, rtp->rt_type, 4);	/* preset 1st type */
	for ( ; rcpp < rcp ; rcpp++) {
		if (strncmp(rcpp->rc_type, rtp->rt_type, 4) != 0) {
			rtp++;		/* we've found a new type */
			bcopy(rcpp->rc_type, rtp->rt_type, 4);
			rtp->rt_offids = (rip - &ri[0]) * sizeof *rip;
		}
		rtp->rt_numids++;
		rip->ri_id = htons(rcpp->rc_id); /* ensure final byte order */
 		if (rcpp->rc_name) {
 			rip->ri_offname = htons(rnp - rn);
 			i = strlen(rcpp->rc_name);
 			if (((rnp - rn) + i + 2) > NAMELEN)
 				abort("namemap exhausted");
			/*
			 * desk accessories start with null, device
			 * drivers start with '.'.
			 */
 			if (strncmp(rcpp->rc_type, "DRVR", 4) == 0
			    && rcpp->rc_name[0] != '.') {
 				*rnp++ = (char)(i + 1);
 				*rnp++ = 0;
			} else {
				*rnp++ = (char) i;
			}
 			bcopy(rcpp->rc_name, rnp, i);
 			rnp += i;
 		}
 		else
 			rip->ri_offname = htons(-1);
		rip->ri_att = rcpp->rc_att;
		rip->ri_offdata = htons(offdata & 0xFFFF);
		rip->ri_offdatahi = ((offdata >> 16) & 0xFF);
		rip++;
		offdata += (rcpp->rc_length + sizeof lendata);
	}
	rtp++;
	/*
	 * Write the file header and pad it out.
	 */
	rf.rf_offdata = htonl(OFFDATA);
	offdata += OFFDATA;
	roundtomap = (offdata & (ROUNDMAP-1));
	if (roundtomap)
		roundtomap = ROUNDMAP - roundtomap;  /* # of pad bytes */
	rf.rf_offmap = offdata + roundtomap;
	rf.rf_lendata = htonl(rf.rf_offmap - OFFDATA);
	rf.rf_offmap = htonl(rf.rf_offmap);
	sizetypes = ((numtypes = rtp - &rt[0]) * sizeof *rtp);
 	if ((rnp - rn) & 1)		/* to be conservative */
 		*rnp++ = 0;
	sizemap = sizeof rm + sizetypes + sizeof numtypes
 	    + ((rip - &ri[0]) * sizeof *rip) + (rnp - rn);
	rf.rf_lenmap = htonl(sizemap);
	fwrite(&rf, sizeof rf, 1, fout);
	i = OFFDATA - sizeof rf;
	do { putc(0, fout); } while (--i);
	/*
	 * correct type list.
	 */
	for (rtpp = &rt[0] ; rtpp < rtp ; rtpp++) {
		rtpp->rt_offids = htons(rtpp->rt_offids 
		    + sizetypes + sizeof numtypes);
		rtpp->rt_numids = htons(rtpp->rt_numids - 1);
	}
	/*
	 * For each resource, write data, file, and bss.
	 */
	for (rcpp = &rescomp[0] ; rcpp < rcp ; rcpp++) {
		lendata = htonl(rcpp->rc_length);
		fwrite(&lendata, sizeof lendata, 1, fout);
		if ((cp = rcpp->rc_data))
			for (i = rcpp->rc_datalen ; i > 0 ; i--)
				putc(*cp++, fout);
		if (rcpp->rc_file)
			for (i = rcpp->rc_filelen ; i > 0 ; i--)
				putc(getc(rcpp->rc_file), fout);
		for (i = rcpp->rc_bss ; i > 0 ; i--)
			putc(0, fout);
		if ((cp = rcpp->rc_rel))
			for (i = rcpp->rc_rellen ; i > 0 ; i--)
				putc(*cp++, fout);
	}
	for (i = roundtomap ; i > 0 ; i--)
		putc(0, fout);
	/*
	 * Write the resource map.
	 */
	rm.rm_offtype = htons(sizeof rm);
 	rm.rm_offname = htons(sizemap - (rnp - rn));
	fwrite(&rm, sizeof rm, 1, fout);
	numtypes--;
	numtypes = htons(numtypes);
	fwrite(&numtypes,sizeof numtypes, 1, fout);
	fwrite(&rt[0], sizeof *rtp, rtp - &rt[0], fout);
	fwrite(&ri[0], sizeof *rip, rip - &ri[0], fout);
	if (rnp - rn)
	 	fwrite(rn, rnp - rn, 1, fout);
}


/*
 * Get next command line.
 * Returns 0 if end of block, 1 if normal line.
 */
getline()
{
	register i;

again:
	if ((fgets(line, sizeof line, fin)) == NULL) {
		fineof++;
		return (0);
	}
	linenum++;
	if ((i = strlen(line)) <= 0)
		return (0);
	if (line[i-1] == '\n')
		line[i-1] = 0;
	lp = line;
	if (*lp == 0)
		return (0);
	if (*lp == '*')
		goto again;
	return (1);
}


/*
 * Abort with message.
 */
abort(s,a,b)
	char *s;
{
	fprintf(stderr, "rmaker: ");
	fprintf(stderr, s, a, b);
	if (linenum)
		fprintf(stderr, "; line number %d", linenum);
	fprintf(stderr, "\n");
	exit(1);
}


/*
 * Check for legal length type.  Fix "STR ".
 */
checktype(s)
	char *s;
{
	register len;

	len = strlen(s);
	if (len < 3 || len > 4)
		abort("bad type");
	if (len == 3) {
		s[3] = ' ';
		s[4] = 0;
	}
}


/*
 * Copy bytes.
 */
bcopy(a, b, n)
	register n;
	register char *a, *b;
{
	if (n <= 0)
		return;
	do { *b++ = *a++; } while (--n);
}


/*
 * Store a short into the data area.
 */
datashort(i)
{
	*(unsigned short *)datap = htons(i);
	datap += sizeof (short);
}


/*
 * Store a long into the data area.
 */
datalong(i)
{
	*(unsigned long *)datap = htonl(i);
	datap += sizeof (long);
}


/*
 * Store string into data area.  If "round" is set, round up length.
 * Returns length of data.
 */
datastring(sp,round)
	char *sp;
{
	char *cp = datap;	/* remember old value to store length */
	int len;
	register c;
	int i;

 	for (datap++, len = 0 ; *sp ; len++) {
 		if ((c = *sp++) != '\\') {
 			*datap++ = c;
 			continue;
 		}
 		switch (c = *sp++) {
 		default:
 			*datap++ = c;
 			continue;
 		case 'b':
 			*datap++ = '\b';
 			continue;
 		case 'f':
 			*datap++ = '\f';
 			continue;
 		case 'n':
 			*datap++ = '\n';
 			continue;
 		case 'r':
 			*datap++ = '\r';
 			continue;
 		case 't':
 			*datap++ = '\t';
 			continue;
 		case '0':
 		case '1':
 		case '2':
 		case '3':
 		case '4':
 		case '5':
 		case '6':
 		case '7':		/* octal constant */
 			*datap = c - '0';		/* stop after      */
			for (i = 2 ; i <= 3 ; i++) {	/* three digits    */
				if ((c = *sp) < '0' || c > '7')	   /* or   */
					break;		/* non-octal digit */
				c -= '0';
				*datap <<= 3;
				*datap |= (c & 0x7);
				*++sp;
			}
			datap++;
 			break;
 		case 'x':		/* hex constant */
 			*datap = 0;
			for (i = 1 ; i <= 2; i++) {	/* stop after two */
				c = *sp;		/* digits or      */
 				if (!isxdigit (c))	/* non-hex digit  */
					break;
				else if (isdigit(c))
					c -= '0';
				else
					c -= isupper(c) ? 'A' - 10 : 'a' - 10;
 				*datap <<= 4;
 				*datap |= (c & 0xf);
				*++sp;
 			}
 			datap++;
 			break;
 		}
 	}
	*cp = len;
	len++;
	if (round && (len & 1)) {
		len++;
		*datap++ = 0;
	}
	return (len);
}


/*
 * Scan next number from line and return it.
 */
scanfn(fmt)
	char *fmt;
{
	int val;

	if (skipsp() == 0 || sscanf(lp,fmt,&val) != 1)
		abort("no number found");
	do {
		lp++;
	} while (*lp != ' ' && *lp != '/0'); /* skip to next number */
	return (val);
}


/*
 * Scan next string from "line" into "token";  return 0 if none left.
 */
scanft()
{
	char *cp;

	if (skipsp() == 0)
		return (0);
	cp = token;
	while (*lp != ' ' && *lp != 0)
		*cp++ = *lp++;
	*cp = 0;
	return (1);
}


/*
 * Skip spaces.  Return 0 if end of line.
 */
skipsp() 
{
	while (*lp == ' ' && *lp != 0) 
  		lp++;
	return(*lp);
}


/*
 * Print the data portion of the current rcp record.
 */
printrcp()
{
 	char *cp;
	int i;
	unsigned int j;

	printf("Type %s, ID %d, length %d, datalen %d\n",
		rcp->rc_type, rcp->rc_id, rcp->rc_length, rcp->rc_datalen);
	cp = rcp->rc_data;		/* pick up the data pointer */
	for (i=0 ; i < rcp->rc_datalen ; i++) {
		j = *cp++ & 0xFF;
		if ((i % 16) == 15) 
			printf("%02X\n",j);
		else 
			printf("%02X ",j);
	}
	printf("\n");
}


#define VAX
#define nohtonl
#ifdef nohtonl	/* if not in library */
#ifdef	VAX
/*
 * "Host" to "net" byte order swappers.
 */
unsigned short htons(a)
	unsigned short a;
{
	unsigned short result;
	register char *sp = (char *)&a;
	register char *dp = (char *)&result;

	dp[1] = *sp++;
	dp[0] = *sp;
	return (result);
}

unsigned long htonl(a)
	unsigned long a;
{
	unsigned long result;
	register char *sp = (char *)&a;
	register char *dp = (char *)&result;

	dp[3] = *sp++;
	dp[2] = *sp++;
	dp[1] = *sp++;
	dp[0] = *sp;
	return (result);
}

#else	/* if running on a native 68K, don't need byte swapping */

unsigned short htons(a)
	unsigned short a;
{
	return (a);
}

unsigned long htonl(a)
	unsigned long a;
{
	return (a);
}

#endif	VAX

#endif	nohtonl



/*
 *	T Y P E   H A N D L E R S
 */


/*
 * Handle string format data.
 */
handstr()
{
	if (getline() == 0)
		abort("missing string");
	datastring(lp,1);
}

/*
 * Handle string list format data.
 */
handstrl()
{
	int count;
	char *backp = datap;
	int nbytes;

	/* Pad for count */
	*datap++ = 0;
	*datap++ = 0;
	/* Process lines */
	for (count = 0; ; ++count)  {
		if (getline() == 0)
			break;
		datastring(lp, 0);
	}
	if (count == 0)
		abort("empty  STR#  string list");
	/* Stuff count where it belongs */
	*backp++ = count >> 8;
	*backp++ = count & 0xFF;
	/* Pad whole resource if odd byte count (assume this is necessary?) */
	nbytes = datap - data;
	if (nbytes & 1)
		*datap++ = '\0';
}
	

/*
 * Handle hexadecimal format data.
 */
handhexa()
{
	char hex[4];
	int val, items, len;

	hex[2] = 0;
	while (getline() != 0) {
		for (len = strlen(lp) ; len > 0 ; ) {
			if (*lp == ' ') {
				lp++;  len--;
				continue;
			}
			strncpy(hex, lp, 2);
			items = sscanf(hex, "%x", &val);
			if (items != 1)
				abort("bad digits");
			*datap++ = val;
			lp += 2;  len -= 2;
			if ((datap - data) >= sizeof data)
				abort("too much data");
		}
	}
	len = datap - data;
	if (len & 1) {
		len++;
		*datap++ = 0;
	}
}


/*
 * Handle program (code) data.
 */
handcode()
{
	register i;
	struct reloc rl;

	/*
	 * setup CODE, id=0 (jumptable)
	 */
	if (rcp->rc_id == 0) {
		rcp[1] = rcp[0];	/* duplicate rescomp entry */
		rcp->rc_att = ATT_PURGEABLE;
		rcp->rc_datalen = rcp->rc_length = sizeof seg0;
		rcp->rc_data = seg0;
		rcp->rc_file = (FILE *)0;
		rcp->rc_bss = 0;
		rcp++;
	}
	/*
	 * setup CODE, id=1 (text/data)
	 */
	if (fread(&bh, sizeof bh, 1, rcp->rc_file) != 1
	    || bh.fmagic != FMAGIC)
		abort("bad b.out header");
	if ((rcp->rc_data = malloc(rcp->rc_datalen = bh.tsize
	    + bh.dsize + sizeof seg1)) == 0)
		abort("code malloc");
	rcp->rc_id++;		/* normally id is now 1 */
	seg1[3] = rcp->rc_id;	/* put id in jump table */
	bcopy(seg1, rcp->rc_data, sizeof seg1);
	rcp->rc_data += sizeof seg1;
	if (fread(rcp->rc_data, rcp->rc_datalen - sizeof seg1,
	    1, rcp->rc_file) != 1)
		abort("code readerror");
	rcp->rc_bss = bh.bsize;
	rcp->rc_length = rcp->rc_datalen + rcp->rc_bss;
	if (!rcp->rc_att)	/* set default attributes if none supplied */
		rcp->rc_att = ATT_PURGEABLE | ATT_LOCKED | ATT_PRELOAD;
	if ((bh.rtsize + bh.rdsize) <= 0)
		abort("b.out must have reloc info");
	fseek(rcp->rc_file, RTEXTPOS, 0);
	if (*(short *)rcp->rc_data != htons(CRTMAGIC))
		abort("no crtmac.s prefix");
	relpnt = rcp->rc_data + 2;		/* start of longrun table */
	rellen = CRTLEN;			/* length of longrun table */
	reloff = 0;
	readrel(0, bh.rtsize/sizeof rl);	/* reloc text */
	readrel(bh.tsize, bh.rdsize/sizeof rl);	/* reloc data */
	*(rcp->rc_data+reloff) = 0377;		/* signals end of reloc data */
	rcp->rc_data -= sizeof seg1;
	fclose(rcp->rc_file);
	rcp->rc_file = 0;
	fprintf(stderr, " text %d, data %d, bss %d, longruns %d\n",
		bh.tsize, bh.dsize, bh.bsize, CRTLEN - rellen);
}


/*
 * Read relocation data and run length encode it.
 */
readrel(off, nrel)
{
	struct reloc rl;
	register char *cp;
	int run, newoff;

	for ( ; nrel > 0 ; nrel--) {
		if (fread(&rl, sizeof rl, 1, rcp->rc_file) != 1)
			abort("error reading reloc");
		if (rl.rsize != RLONG || (rl.rpos & 1)
		    || rl.rsymbol || rl.rsegment == REXT)
			abort("impossible relocation");
		newoff = (rl.rpos + off);
		run = (newoff - reloff) >> 1;
		if (reloff == 0 || run >= 0377) {
			*(long *)relpnt = htonl(newoff);
			relpnt += sizeof (long);
			if (--rellen <= 0)
				abort("too many longruns");
		} else {
			*(rcp->rc_data+reloff) = run;
		}
		reloff = newoff;
	}
}


/*
 * DRVR, INIT, PACK, CDEF, MDEF, and WDEF all have essentially
 * the same format.  The only difference is that DRVR's require
 * a different offset to the entry point.
 */

#define	DRVROFF	50	/* offset of longruns in DRVR resource */
			/* Interpretation of that last comment for
			   the rest of us:  The entry point in
			   crtdrvr.s is the 50th byte. */
#define	DRVRMAGIC

#define PROCOFF 0	/* zero offset for crtwdef.s entry point */

handdrvr()
{
	relocate(DRVROFF);
}

handproc()
{
	relocate(PROCOFF);
}


/*
 * Relocate a resource.
 */
relocate(off)
{
	register i;
	struct reloc rl;

	if (fread(&bh, sizeof bh, 1, rcp->rc_file) != 1
	    || bh.fmagic != FMAGIC)
		abort("bad b.out header");
	if((rcp->rc_rellen = (bh.rtsize + bh.rdsize) / sizeof(rl)) & 1)
		rcp->rc_rellen++;
	if ((rcp->rc_data = malloc((rcp->rc_datalen = bh.tsize
	    + bh.dsize) + rcp->rc_rellen)) == 0)
		abort("text malloc");
	rcp->rc_rel = rcp->rc_data + rcp->rc_datalen;
	if (fread(rcp->rc_data, rcp->rc_datalen, 1, rcp->rc_file) != 1)
		abort("text readerror");
	rcp->rc_bss = bh.bsize;
	rcp->rc_length = rcp->rc_datalen + rcp->rc_bss + rcp->rc_rellen;
	if ((bh.rtsize + bh.rdsize) <= 0)
		abort("b.out must have reloc info");
	fseek(rcp->rc_file, RTEXTPOS, 0);
	if (*(short *)(rcp->rc_data+off) != htons(RELMAGIC))
		abort("no crtxxx.s prefix");
	relpnt = rcp->rc_data + off + 2;	/* start of longrun table */
	rellen = RELLEN;			/* length of longrun table */
	*(long *)relpnt = htonl(rcp->rc_length
			  - rcp->rc_rellen);	/* offset to reloc table */
	relpnt += sizeof(long);
	relrel = rcp->rc_rel;			/* start of reloc table */
	reloff = 0;
	rdreloc(0, bh.rtsize/sizeof rl);	/* reloc text */
	rdreloc(bh.tsize, bh.rdsize/sizeof rl);	/* reloc data */
	*relrel = 0377;				/* signals end of reloc data */
	fclose(rcp->rc_file);
	rcp->rc_file = 0;
	fprintf(stderr, " %s text %d, data %d, bss %d, reloc %d, longruns %d\n",
		rcp->rc_type, bh.tsize, bh.dsize, bh.bsize, rcp->rc_rellen, RELLEN - rellen);
}


/*
 * Read relocation data and run length encode it in a dynamically relocatable
 * form.
 */
rdreloc(off, nrel)
{
	struct reloc rl;
	register char *cp;
	int run, newoff;

	for ( ; nrel > 0 ; nrel--) {
		if (fread(&rl, sizeof rl, 1, rcp->rc_file) != 1)
			abort("error reading reloc");
		if (rl.rsize != RLONG || (rl.rpos & 1)
		    || rl.rsymbol || rl.rsegment == REXT)
			abort("impossible relocation");
		newoff = (rl.rpos + off);
		run = (newoff - reloff) >> 1;
		if (reloff == 0 || run >= 0377) {
			*(long *)relpnt = htonl(newoff);
			relpnt += sizeof (long);
			if (--rellen <= 0)
				abort("too many longruns");
			if(reloff)
				*relrel++ = 0;
		} else {
			*relrel++ = run;
		}
		reloff = newoff;
	}
}


/*
 * Handle dialog template (DLOG).
 */

/*
 * This structure is defined in toolintf.h, but to avoid byte swap
 * and alignment problems, we fill it "by hand".
 *
 *	typedef	struct	{
 *		Rect	boundsRect;
 *		short	procID;
 *		char	visible;
 *		char	filler1;
 *		char	goAwayFlag;
 *		char	filler2;
 *		long	refCon;
 *		short	itemsID;
 *		Str255	title;
 *	} DialogTemplate;
 */

handdlog()
{
	int vis,go,pid,ref;
	register i;

	if (getline() == 0) 
		abort("no dlog rectangle");
	for (i=0 ; i<4 ; i++) 		/* parse 4 ints - rectangle */
		datashort(scanfn("%d"));
	if (getline() == 0) 
		abort("no dlog vis/proc");
	scanft();
	vis = (token[0] == 'V' ? 1 : 0);
	pid = scanfn("%d");
	scanft();
	go = (token[0] == 'G' ? 1 : 0);
	ref = scanfn("%d");
	datashort(pid);
	*datap++ = vis;  *datap++ = 0;
	*datap++ = go;   *datap++ = 0;
	datalong(ref);
	if (getline() == 0)
		abort("missing Item list ID");
	datashort(scanfn("%d"));
	if (getline() != 0)
		datastring(lp,1);
	else
		datashort(0);
}


/*
 * Handle alert template.
 */

/*
 * This structure is defined in toolintf.h, but to avoid byte swap
 * and alignment problems, we fill it "by hand".
 *
 *	typedef	struct {
 *		Rect	boundsRect;
 *		short	itemsID;
 *		short	stages;
 *	} AlertTemplate;
 */

handalrt() 
{
	int i;

	getline();
	for (i=0 ; i<4 ; i++) 		/* parse 4 ints - rectangle */
		datashort(scanfn("%d"));
	getline();
	datashort(scanfn("%d"));
	getline();
	datashort(scanfn("%x"));
}


/* 
 * Handle Dialog and Alert Item Lists (Type DITL)
 *
 */

/*
 * Structure of an item list.
 *
 *	struct {
 *		long	zero;		placeholder for handle 
 *		Rect	itemrect;
 *		char	itemtype;
 *		char	itemlength;
 *		...  			followed by variable length data
 *	}
 */

struct ditlkeys {
	char *ditl_name;
	int ditl_value;
} ditlkeys[] = {
	"CtrlItem",ctrlItem,		/*  used in conjunction with: */
	"BtnCtrl",btnCtrl,		/*   a button control */
	"ChkCtrl",chkCtrl,		/*   checkbox  */
	"RadCtrl",radCtrl,		/*   etc... */
	"ResCtrl",resCtrl,
 	"RadioItem",radCtrl+ctrlItem,
 	"ChkItem",chkCtrl+ctrlItem,
	"StatText",statText,
	"EditText",editText,
	"IconItem",iconItem,
	"PicItem",picItem,
	"UserItem",userItem,
	"ItemDisable",itemDisable,
	"BtnItem",btnCtrl+ctrlItem,	/* abbreviation */
	"Enabled",0,
	"Disabled",itemDisable,		/* synonym */
	"Disable",itemDisable,		/* synonym */
	"ItemDisabled",itemDisable,	/* synonym */
	0,0
};


handditl()
{
	char *lenp;
	int i,len;
	int val,types;
	register struct ditlkeys *dk;

	/* first line is item count, drop in count-1 */
	if (getline() == 0 || (val = scanfn("%d")) < 1)
		abort("bad DITL item count");
	datashort(val-1);

	/* for each item */
	for ( ; val > 0 ; val--) {
		datalong(0);
 		if (getline() == 0)		/* line with item types */
			abort("Missing DITL item type");
		types = 0;
		while (scanft()) {
			for (dk = &ditlkeys[0] ; dk->ditl_name ; dk++)
				if (strcmp(dk->ditl_name,token) == 0)
					goto found;
			abort("bad DITL item type %s",token);
		found:
			types += dk->ditl_value;
		}
 		if (getline() == 0)
			abort("Missing DITL rectangle");		
		for ( i=0 ; i < 4 ; i++)
			datashort(scanfn("%d"));
		*datap++ = types;
 		if ((getline() == 0) && (types&(~itemDisable) != editText))
			abort("Missing DITL data");
		skipsp();

		lenp = datap++;		/* remember spot for length */
		types &= ~itemDisable;		/* don't care about this bit */

		switch (types) {	

		case iconItem:		/* 2 byte resource ID */
		case picItem:
		case ctrlItem+resCtrl: 
			datashort(scanfn("%d"));
			*lenp = sizeof (short);
			break;	
		
		case userItem:
			*lenp = 0;		/* is nothing */
			break;

		case ctrlItem+btnCtrl:
		case ctrlItem+chkCtrl:
		case ctrlItem+radCtrl:
		case statText:	
		case editText:
			len = strlen(lp);
			strcpy(datap,lp);
			datap += len;
			if (len & 1) {
				len++;
				*datap++ = 0;
			}
			*lenp = len;
			break;
		} 
		if (getline() != 0)
			abort("Expected blank line in DITL");
	}
}


/*
 * Handle window template (WIND).
 */

/*
 *	typedef	struct	{
 *		Rect	boundsRect;
 *		short	procID;
 *		char	visible;
 *		char	filler1;
 *		char	goAwayFlag;
 *		char	filler2;
 *		long	refCon;
 *		Str255	title;
 *	} WindowTemplate;
 */

handwind()
{
	int vis,go,pid,ref;
	char title[128];
	register i;

	getline();
	skipsp();
	strcpy(title,lp);
	getline();
	for (i=0 ; i<4 ; i++) 		/* parse 4 ints - rectangle */
		datashort(scanfn("%d"));
	getline();
	scanft();
	vis = (token[0] == 'V' ? 1 : 0);
	scanft();
	go = (token[0] == 'G' ? 1 : 0);
	getline();
	pid = scanfn("%d");
	getline();
	ref = scanfn("%d");
	datashort(pid);
	*datap++ = vis;  *datap++ = 0;
	*datap++ = go;   *datap++ = 0;
	datalong(ref);
	datastring(title,1);
}


/*
 * Handle control template (CNTL).
 */

/*
 *	typedef	struct	{
 *		Rect	boundsRect;
 *		short	value;
 *		char	visible;
 *		char	filler1;
 *		short	max;
 *		short	min;
 *		short	procID;
 *		long	refCon;
 *		Str255	title;
 *	} ControlTemplate;
 */

handcntl()
{
	int vis,min,max,pid,ref;
	char title[128];
	register i;

	getline();
	skipsp();
	strcpy(title,lp);
	getline();
	for (i=0 ; i<4 ; i++) 		/* parse 4 ints - rectangle */
		datashort(scanfn("%d"));
	getline();
	scanft();
	vis = (token[0] == 'V' ? 1 : 0);
	getline();
	pid = scanfn("%d");
	getline();
	ref = scanfn("%d");
	getline();
	datashort(scanfn("%d"));
	*datap++ = vis;  *datap++ = 0;
	min = scanfn("%d");
	max = scanfn("%d");
	datashort(max);
	datashort(min);
	datashort(pid);
	datalong(ref);
	datastring(title,1);
}


/*
 * Handle menu template (MENU).
 */

/*
 *	typedef	struct	{
 *		short	menuID;
 *		long	fill1,fill2;	placeholder
 *		long	enableFlags;
 *		Str255	title;
 *	  for each menu item:
 *		Str255	text;
 *		char	icon#;
 *		char	keyboardequiv;
 *		char	mark;
 *		char	textstyle;
 *	  finally:
 *		char	zero;		end of items.
 *	}
 */

handmenu()
{
	int iconid, styleid, cmdid, markid;
	int *flagsp, flags, item;
	register char *cp,*dp,*sp;
	char itext[128];
	static char styles[] = "BIUOS";

	datashort(rcp->rc_id);
	datalong(0);
	datalong(0);
	flagsp = (long *)datap;	/* remember where the flags were */
	flags = -1;		/* enable all items */
	datalong(-1);	/* placeholder */
	getline();
	scanft();
	datastring(token,0);
	for (item = 1 ; getline() && item < 32 ; item++) {
		skipsp();
		iconid = styleid = cmdid = markid = 0;
		for (cp = lp, dp = itext ; *cp ; cp++) {
			switch (*cp) {
			default:
				*dp++ = *cp;
				break;

			case '(':
				flags &= ~(1<<item);
				break;

			case '^':
				cp++;
				iconid = *cp;
				break;
			
			case '/':
				cp++;
				cmdid = *cp;
				break;

			case '!':
				cp++;
				markid = *cp;
				break;

			case '<':
				cp++;
				sp = index(styles, *cp);
				if (sp == 0)
					abort("BIUOS expected after <");
				styleid |= (1 << (sp-styles));
				break;
			}
		}
		*dp++ = 0;
		datastring(itext,0);
		*datap++ = iconid;
		*datap++ = cmdid;
		*datap++ = markid;
		*datap++ = styleid;
	}
	/* end of items */
	*datap++ = 0;
	*flagsp = htonl(flags);
}


/*
 * Handle postscript strings ("Laser Prep" file).
 *  First line is number of lines that follow.
 *  Each following line is a string.
 */
handpost()
{
	int count;
	int nbytes;

	if (getline() == 0)	/* get string count */
		abort("missing string in POST");
	sscanf(lp, "%d", &count);
	*datap++ = count >> 8;
	*datap++ = count & 0xff;
	while (count--) {
		getline();
		datastring(lp,0);
	}
	/* Pad whole resource if odd byte count (is this necessary?) */
	nbytes = datap - data;
	if (nbytes & 1)
		*datap++ = '\0';
}
@//E*O*F rmaker.c//
chmod u=r,g=r,o=r rmaker.c
 
echo x - rmaker.1
sed 's/^@//' > "rmaker.1" <<'@//E*O*F rmaker.1//'
@.TH RMAKER local "10/20/84"
@.UC 4
@.SH NAME
rmaker \- resource maker (compiler) for Macintosh
@.SH SYNOPSIS
@.B rmaker
file.rc
@.br
@.B rmaker
[
@.B \-d
type
] file.rc
@.SH DESCRIPTION
@.I Rmaker
reads an ascii resource compiler input file "file.rc" and 
produces a Macintosh executable binary "file.rsrc".
See the Inside Mac manual "Putting Together a Macintosh Application"
for a description of this format and process.
It is also helpful to look at one of the example '.rc' files in
the SUMacC 'mac/' source directory.
@.PP
Most of the commonly used resource types are implemented:
STR, STR#, HEXA, CODE, DRVR, ALRT, DITL, DLOG, WIND,
MENU, CNTL, ICON, ICN#, CURS, PAT, INIT, PACK, CDEF,
MDEF, WDEF, POST, FKEY, and PROC.
See the BUGS section below for exceptions.
@.PP
The optional \-d (debug) switch will list out in hex the contents
of all resources matching that four letter type.
@.PP
The usual backslash escape sequences are allowed in strings (\\[bfnrt] and
\\ooo where the "o"'s are octal digits.)  Also, \\xhh is allowed where "h" is
a hexadecimal digit.
@.SH SEE ALSO
"Putting Together a Macintosh Application"
(note:  this manual is no longer part of Inside Macintosh)
@.PP
"Format of the SUMacC Resource Compiler Input File"  (basically
the same as IM, but specific details of the SUMacC rmaker are
covered.)  This document is included in this release.
@.SH BUGS
If you have more than one resource of the same type, they must all
be grouped together in the file, and each resource must begin with the "Type"
statement.  
@.PP
Types NOT implemented:  PICT, PAT#, FREF, BNDL, FONT, GNRL.
You can always use an "inherited type" of HEXA (e.g. Type BNDL = HEXA)
to simulate unimplemented types.  GNRL would be even better for this
(if someone would only implement it!)
@.PP
Rmaker input has no "standard" format.  Where possible, the format of the
Lisa Rmaker has been used.  In the case of STR#, the format is different
than that of the Mac Rmaker.  See "Format of the SUMacC Resource Compiler
Input File."
@.PP
If you get the message "impossible relocation", it usually means your
b.out had some undefined external references;  check the error output
from 'ld', you probably misspelled some global or routine name.
@.PP
If you get a message similar to "no crtxxx.s prefix", you have probably
linked with the wrong type of crt*.s file for your DRVR or PROC (WDEF, INIT,
etc.).  The proper files are included in this release.
@//E*O*F rmaker.1//
chmod u=r,g=r,o=r rmaker.1
 
echo x - rmaker.doc
sed 's/^@//' > "rmaker.doc" <<'@//E*O*F rmaker.doc//'
_______________________________________________________________________________
FORMAT OF THE SUMACC RESOURCE COMPILER INPUT FILE
	Modified 07/01/85 by Brian Powell	Adapt for SUMacC
		 07/06/85    Brian Powell	mention backslash escapes
_______________________________________________________________________________

You'll need to create a resource file for your application.  This is
done with the Resource Compiler, and you'll have among your working
files an input file to the Resource Compiler.  *** In the future,
you'll be able to use the Resource Editor. ***  One convention for
naming this input file is to give it the name of your application
followed by ".rc"  (Such as samp.rc)

One of the entries in the input file specifies the name to be given to
the output resource file; you'll enter the application name and ."rsrc".
Another entry tells which file the application code segments are to be read
from.  (As discussed in the Resource Manager manual, the code segments are
actually resources of the application.)  You'll enter the name of the
Linker output file specified in the Exec file for building your
application, as described in the next section.

If you don't want to include any resources other than the code
segments, you can have a simple input file like this:

	*  samp.rc -- Resource input for sample application
	*	      Written by Macintosh User Education  1/13/84

	samp.rsrc

	Type SAMP = STR 
	  ,0
	Samp Version 1.0 -- January 13, 1984

	Type CODE
	  samp.o,0

This tells the Resource Compiler to write the resulting resource file
to samp.rsrc and to read the application code segments from
samp.o in the working directory.  It also specifies the file's signature
and version data, which the Finder needs.  *** (Documentation on the
signature and version data is forthcoming.) ***

It's a good idea to begin the input file with a comment that describes
its contents and shows its author, creation date, and other such
information.  Any line beginning with an asterisk (*) is ignored ***
(except within a menu, for now) ***.  The Resource Compiler also
ignores the following:

   - leading spaces (except before the text of a string resource)

   - embedded spaces (except in file names, titles, or other text
     strings)

   - blank lines (except for those indicated as required)

The first line that isn't ignored specifies the name to be given to the
resulting resource file.  Then, for each type of resource to be
defined, there are one or more "Type statements".  A Type statement
consists of the word "Type" followed by the resource type (without
quotes) and, below that, an entry of following format for each
resource:

	file name/resource name, resource ID (resource attributes)
	type-specific data

The punctuation shown here is typed as part of the format.  You must
always provide a resource ID.  Specifications other than the resource
ID may or may not be required, depending on the resource type:

   - Either there will be some type-specific data defining the resource
     or you'll give a file name indicating where the resources will be
     read from.  You can specify a directory; if not, the working
     directory is assumed.  Even in the absence of a file name, you must
     include the comma before the resource ID.

   - You specify a resource name along with the file name for fonts and
     drivers.  The Menu Manager procedures AddResMenu and InsertResMenu
     will put these resource names in menus.  Enter the names in the
     combination of uppercase and lowercase that you want to appear in
     the menus.

   - Resource attributes in parentheses are optional for all types.
     They're given as a number equal to the value of the resource
     attributes byte, and 0 is assumed if none is specified.  (See the
     Resource Manager manual for details.)  For example, for a resource
     that's purgeable but has no other attributes set, the input will
     be "(32)"; for one that's purgeable and in the system heap, it
     will be "(96)".

The formats for the different types of resources are best explained by
example.  Some examples are given below along with comments that
provide further explanation.  Here are some points to remember:

   - In every case, resource attributes in parentheses may be specified
     after the resource ID.

   - All numbers are base 10 except where hexadecimal is indicated.

   - The Type statements must be grouped by type in the input file.

   - In general, all strings may include backslash escape characters.
(\b, \f, \n, \r, \t, \377, \x7a, etc.)


Type WIND			Window template
  ,1				Resource ID
  Status Report			Window title
  40 80 120 300			BoundsRect (top left bottom right)
  Visible GoAway		For FALSE, use Invisible or NoGoAway
  0				ProcID (window definition ID)
  0				RefCon (reference value)

Type MENU			Menu (standard, enabled)
  ,2				Resource ID (menu ID)
  Edit				Menu title
    Cut/X			Menu items, one per line, with meta-
    Paste/Z			  characters, ! alone for check mark
    (-				You cannot specify a blank item; use (-
    Word Wrap!			  for a disabled continuous line.
  				Blank line required at end of menu
Type CNTL			Control template
  ,1				Resource ID
  Help				Control title
  55 20 75 90			BoundsRect
  Visible			For FALSE, use Invisible
  0				ProcID (control definition ID)
  1				refCon (reference value)
  0 0 0				Value minimum maximum

Type DLOG			Dialog template
  ,1				Resource ID
  100 100 190 250		BoundsRect
  Visible 1 GoAway 0		1 is procID, 0 is refCon
  3				Resource ID of item list
  				Title (none in this case)
Type ALRT			Alert template
  ,1				Resource ID
  120 100 190 250		BoundsRect
  5				Resource ID of item list
  F721				Stages word in hexadecimal

Type DITL			Item list in dialog or alert
  ,3				Resource ID
  5				Number of items
  BtnItem Enabled		Also:  ChkItem, RadioItem
  60 10 80 70			Display rectangle
  Start				Title
  				Blank line required between items
  CtrlItem Enabled		Control defined in control template
  60 10 80 100			Display rectangle
  1				Resource ID of control template

  StatText Disabled		Also:  EditText
  10 93 26 130			Display Rectangle
  Seed				The text (may be blank if EditText)

  IconItem Disabled		Also:  PicItem
  10 24 42 56			Display rectangle
  1				Resource ID of icon

  UserItem Disabled		Application-defined item
  20 50 60 85			Display rectangle

Type ICON			Icon
  ,1				Resource ID
  0380 0000			The icon in hexadecimal (32 such lines)
    . . .			  altogether)
  1EC0 3180

Type ICN#			Icon list
  ,128				Resource ID
  0001 0000			The icons in hexadecimal (32 such lines
    . . .			  altogether for each icon)
  0002 8000
				Blank line required after last icon
Type CURS			Cursor
  ,300				Resource ID
  7FFC . . . 287F		The data:  64 hex dgits on one line
  0FC0 . . . 1FF8		The mask:  64 hex digits on one line
  0008 0008			The hotSpot in hexadecimal (v h)

Type PAT 			Pattern
  ,200				Resource ID
  AADDAA66AADDAA66		The pattern in hexadecimal

Type STR 			String
  ,36				Resource ID
This is your string		The string on one line (leading spaces
				  not ignored)
Type STR#			String list
  ,1				Resource ID
This is string one		the strings...
This is string two
This is string three
				Blank line required following STR#
Type DRVR			Desk accessory or other I/O driver
  Monkey/Monkey,17 (32)		File name/resource name, resource ID
				  [ SEE NOTE 1 BELOW ]
Type FREF = HEXA		File reference *** see note 4 below ***
  ,128				Resource ID
  4150504C			File type APPL
  0000				Local ID of icon
  05544746494C			File name "TgFil" (00 if none)

Type BNDL = HEXA		Bundle *** see note 4 below ***
  ,128				Resource ID
  53414D70 0000			Bundle owner SAMP 0
  0001				Two types in bundle (ICN# and FREF)
  49434E23 0000			ICN# number of resources minus 1
  0000 0080			Local ID 0 maps to resource ID 128
  46524546 0000			FREF number of resources minus 1
  0000 0080			Local ID 0 maps to resource ID 128

Type HEXA			Any butes specified here
  ,9				Resource ID
  526FEEC942E78EA4		The bytes in hexadecimal (any number
  0F4C				 total, any nuber per line)
				Blank line required at end
Type CODE			Application code segments
  samp.o,0			Linker output file name, resource ID
				[ SEE NOTE 3 BELOW ]
Type POST			Postscript resource
  ,1				Resource ID
4				Number of strings
This is string one		The strings...
This is string two
Third string
Bench warmer			Apple's joke, not mine.

Notes:

 1.  The Resource Compiler adds a NUL character (ACII code 0) at the
     beginning of the name you specify for a 'DRVR' type of resource
     if it doesn't begin with a period ('.').  Therefore, device drivers
     should start with a '.', and desk accessories with any other character.
     This inclusion of a nonprinting character avoids confilict with
     file names that are the same as the names of desk accessories.

 3.  For a 'CODE' type of resource, the resource ID you specify is
     ignored.  The Resource Compiler always creates two resources of
     this type, with ID numbers 0 and 1, and will create additional
     ones numbered sequentially from 2 if your program is divided
     into segments.

 4.  *** Temporary:  The file reference and bundle resources are for
     extablishing an interface with the Finder and are not yet
     documented;  se Macintosh Technical Support for more information.
     A future version of the Resource Compiler will recognize these
     resource types and let you enter them in a more convenient format
     than shown here.  ***

The Type statement for a resource of type 'WDEF', 'MDEF', 'CDEF',
'FKEY' *** function key code ***, 'PACK', 'PROC', or 'INIT'  has the
same format as for 'CODE':  only a file name and a resource ID are
specified.  The file contains the compiled code of the resource.

You can also define a new resource type that inherits the properties of
a standard type.  For example,

	Type XDEF = WDEF

defines the new type 'XDEF', which the Resource Compiler treats exactly
like 'WDEF'.  The next line would contain a file name and resource ID
just as for a 'WDEF' resource.  As another example, suppose you want to
define a resource type 'MINE' that consists of any bytes specified in
hexadecimal in the Resource Compiler input file; you can use

	Type MINE = HEXA

followed by the resource ID and bytes on the subsequent lines.

If your application is going to write to the resulting resource file as
well as read it, you should place the Type statement for the code
segments at the end of the input file.  In general, any resources that
the application might change and write out to the resource file should
be listed first in the input file, and any resources that won't be
changed (like the code segments) should be listed last.  The reason for
this is that the Resource Compiler stores resources in the reverse of
the order that they're listed, and it's more efficient for the Resource
Manager to do file compaction if the changed resources are at the end
of the resource file.
@//E*O*F rmaker.doc//
chmod u=r,g=r,o=r rmaker.doc
 
echo x - crtdrvr.s
sed 's/^@//' > "crtdrvr.s" <<'@//E*O*F crtdrvr.s//'
|
| crtdrvr.s - dynamically relocating C runtime startoff for Mac desk accessory
|
| Copyright (C) 1984, Stanford Univ. SUMEX project
| May be used but not sold without permission.
|
| history
| 07/20/84	Croft	Created.
| 10/24/84	Smith	Customize for test
| 01/10/85	Moy	Add dynamic rellocation
|
Open	=	0
Prime	=	4
Ctl	=	8
Status	=	12
Close	=	16

	.data
	.text
	.globl	_savea5
	.globl	drvr
	.globl	drvrOpen,drvrPrime,drvrCtl,drvrStatus,drvrClose

| driver header

drvr:
	.word	0x2400		| enable control, need time
	.word	2		| 30 times a second
	.word	0x0142		| mouseDown, update, activate events
	.word	0		| no menu
doffset:
	.word	dopen-drvr
	.word	dprime-drvr
	.word	dctl-drvr
	.word	dstatus-drvr
	.word	dclose-drvr
	.byte	6
|	.ascii	"\0Sleep"
	.byte	0
	.ascii	"Sleep"
	.blkb	25		| 32 bytes total for name

	jra	dopen		| magic number
reloff:	.long	0
	.long	0,0,0,0,0,0,0,0,0,0		| longruns from rmaker
reladdr:.long	0
_savea5:.long	0

|
| driver entry points
|
dopen:
	moveq	#Open,d0
	bras	.L21
dprime:
	moveq	#Prime,d0
	bras	.L21
dctl:
	moveq	#Ctl,d0
	bras	.L21
dstatus:
	moveq	#Status,d0
	bras	.L21
dclose:
	moveq	#Close,d0

@.L21:
	moveml	#0x3ffc,sp@-
	lea	pc@([reladdr-.-2]),a2	| reloc addr
	lea	pc@([drvr-.-2]),a3	| reloc factor
	movl	a3,d1
	andl	#0xffffff,d1		| clear out trash in high byte
	subl	a2@,d1			| have we moved?
	beqs	call
	addl	d1,a2@			| adjust reladdr
	lea	pc@([reloff-.-2]),a3
	addl	d1,a3@			| adjust addr of reloc table
	movl	a3@+,a4			| get addr of reloc table
	addl	d1,a3@			| adjust 1st relocation
	movl	a3@+,a2			| pickup 1st relocation

|
| a2 = current reloc address
| a3 = next longrun address
| a4 = next reloc table address
| d1 = relocation factor
|
|	for(;;) {
|		*(u_long *)a2 += (u_long)d1;
|		if ((i = (*a4++ & 0377)) == 0) {
|			*a3 += d1;
|			a2 = *a3++;
|			continue;
|		}
|		if (i == 0377)
|			goto start;
|		a2 += (i << 1);
|	}
@.L16:
	addl	d1,a2@
	movb	a4@+,d7
	andl	#255,d7
	bnes	.L19
	addl	d1,a3@
	movl	a3@+,a2
	bras	.L16
@.L19:
	cmpl	#255,d7
	beqs	.L18
	roll	#1,d7
	addl	d7,a2
	bras	.L16
|
| if shift button is pressed on entry, beep and hang around for an NMI.
|
@.L18:
	btst	#0,0x17b
	beqs	.L31		| if not pressed
	movw	#1,sp@-		| sysbeep, duration 1
	.word	/A9C8
	moveq	#1,d2
@.L22:
	tstl	d2
	bnes	.L22		| hang, waiting for NMI
@.L31:
	movl	a5,_savea5
call:
	movl	a1,sp@-
	movl	a0,sp@-
	movl	#jmptab,a0
	addl	d0,a0
	movl	a0@,a0
	jsr	a0@
	addql	#8,sp
	moveml	sp@+,#0x3ffc
	cmpl	#0x40000000,d0
	bnes	done
	clrl	d0
	rts
jiodone = 0x8fc
done:
	movl	jiodone,sp@-
	rts
jmptab:	.long	drvrOpen
	.long	drvrPrime
	.long	drvrCtl
	.long	drvrStatus
	.long	drvrClose
@//E*O*F crtdrvr.s//
chmod u=r,g=r,o=r crtdrvr.s
 
echo x - crtproc.s
sed 's/^@//' > "crtproc.s" <<'@//E*O*F crtproc.s//'
|
| crtproc.s - dynamically relocating C runtime startoff for PROCs.
|		This is a sample window definition procedure.
|
| Copyright (C) 1984, Stanford Univ. SUMEX project
| May be used but not sold without permission.
|
| history
| 01/10/85	Moy	Created
| 07/01/85	Powell	renamed from crtwdef.s to crtproc.s
|

	.data
	.text
	.globl	_savea5
	.globl	wdef
	.globl	MyWindow

wdef:	jra	.L21		| magic number
reloff:	.long	0
	.long	0,0,0,0,0,0,0,0,0,0		| longruns from rmaker
reladdr:.long	0
_savea5:.long	0

@.L21:
	moveml	#0x3ffc,sp@-
	lea	pc@([reladdr-.-2]),a2	| reloc addr
	lea	pc@([wdef-.-2]),a3	| reloc factor
	movl	a3,d1
	andl	#0xffffff,d1		| clear out trash in high byte
	subl	a2@,d1			| have we moved?
	beqs	call
	addl	d1,a2@			| adjust reladdr
	lea	pc@([reloff-.-2]),a3
	addl	d1,a3@			| adjust addr of reloc table
	movl	a3@+,a4			| get addr of reloc table
	addl	d1,a3@			| adjust 1st relocation
	movl	a3@+,a2			| pickup 1st relocation

|
| a2 = current reloc address
| a3 = next longrun address
| a4 = next reloc table address
| d1 = relocation factor
|
|	for(;;) {
|		*(u_long *)a2 += (u_long)d1;
|		if ((i = (*a4++ & 0377)) == 0) {
|			*a3 += d1;
|			a2 = *a3++;
|			continue;
|		}
|		if (i == 0377)
|			goto start;
|		a2 += (i << 1);
|	}
@.L16:
	addl	d1,a2@
	movb	a4@+,d7
	andl	#255,d7
	bnes	.L19
	addl	d1,a3@
	movl	a3@+,a2
	bras	.L16
@.L19:
	cmpl	#255,d7
	beqs	.L20
	roll	#1,d7
	addl	d7,a2
	bras	.L16
@.L20:
	movl	a5,_savea5
call:
	moveml	sp@+,#0x3ffc
	jmp	MyWindow
@//E*O*F crtproc.s//
chmod u=r,g=r,o=r crtproc.s
 
exit 0

guido@boring.UUCP (07/11/85)

Here are some changes to the improved rmaker source that was posted last week
by Brian Powell.  Immediately after I received his version I communicated
a compatibility problem with the \ escapes, and got his consent to post
my version.  There are some more changes to be found in this diff listing
(see below for a detailed account).

Brian and I hope this posting makes it in time to most of the rmaker hackers
to prevent proliferation of different versions again.  (Readers of
net.micro.mac may remember my plea for compatibility in The Case Of The
Harmonic Series Benchmarks :-).

I also sent a copy to Croft -- hope he agrees too.

	Guido van Rossum, CWI, Amsterdam (the capital of Copenhagen :-)
	guido@mcvax.UUCP

---------------------------------------------------------------------------

A detailed account of the changes:

- \ escapes.  Inside Mac documents only the following form of \ escapes:
  \XX where XX are two hexadecimal digits.  I (and Brian) thought it better
  to stick to this convention rather than use some fancy incompatible
  scheme.  (After all, it rarely is necessary to include hex codes at all.)
  IM doesn't document the effect of \ followed by garbage.  I chose for
  letting the backslash in the string, except that \\ is replaced by a
  single \.

- POST is now identical to STR#.  Brian asked me to put this change in,
  because he had found out that blank lines really aren't necessary
  (I have no expertise in this field).

- Finally, I added a mod which makes it easier to change the values of CRTLEN
  and RELLEN (CRTMAGIC and RELMAGIC are automatically adjusted).  I once
  needed this to process a huge binary (~200 kbytes) Just recompile rmaker.c
  with -DCRTLEN=20 and use a version of crtmac.s with 20 long zeros instead
  of 10.

Here are the diffs -- use patch to process.

---------------------------------------------------------------------------

*** posted-rmaker.c	Wed Jul 10 11:00:26 1985
--- rmaker.c	Thu Jul 11 10:59:49 1985
***************
*** 36,41
   * 06/29/85	Powell		Added PROC.
   * 07/06/85	Powell		Fix tolower bug in backslash code.
  				Limit \ooo to three digits, \xhh to two.
   */
  
  #include <stdio.h>

--- 36,47 -----
   * 06/29/85	Powell		Added PROC.
   * 07/06/85	Powell		Fix tolower bug in backslash code.
  				Limit \ooo to three digits, \xhh to two.
+  *** Branch 4
+  * 07/09/85	GvR		Allow CRTLEN and RELLEN to be overridden
+  *				on compilation with -D; made CRTMAGIC and
+  *				RELMAGIC self-adjusting; changed \ escapes
+  *				to hex (as in Inside Mac).
+  * 11/09/85	GvR		Changed POST to be identical to STR#.
   */
  
  #include <stdio.h>
***************
*** 39,45
   */
  
  #include <stdio.h>
- #include <ctype.h>
  #include <res.h>
  #include <b.out.h>
  #include <quickdraw.h>

--- 45,50 -----
   */
  
  #include <stdio.h>
  #include <res.h>
  #include <b.out.h>
  #include <quickdraw.h>
***************
*** 60,66
  };				/* "standard" segment 0 jump header/table */
  char	seg1[sizeof(struct codehead)] = { 0,0,  0,1 };
  
! #define	CRTMAGIC 0x602C		/* jump at beginning of crtmac.s */
  #define	CRTLEN	10		/* length of relocation table in crtmac.s */
  #define	RELMAGIC 0x6034		/* jump at beginning of crt*.s */
  #define	RELLEN	12		/* length of relocation table in crt*.s */

--- 65,71 -----
  };				/* "standard" segment 0 jump header/table */
  char	seg1[sizeof(struct codehead)] = { 0,0,  0,1 };
  
! #ifndef CRTLEN
  #define	CRTLEN	10		/* length of relocation table in crtmac.s */
  #endif CRTLEN
  
***************
*** 62,68
  
  #define	CRTMAGIC 0x602C		/* jump at beginning of crtmac.s */
  #define	CRTLEN	10		/* length of relocation table in crtmac.s */
! #define	RELMAGIC 0x6034		/* jump at beginning of crt*.s */
  #define	RELLEN	12		/* length of relocation table in crt*.s */
  
  #define	NRESCOMP 50		/* max number of resources per compile */

--- 67,77 -----
  
  #ifndef CRTLEN
  #define	CRTLEN	10		/* length of relocation table in crtmac.s */
! #endif CRTLEN
! 
! #define	CRTMAGIC (0x6004+4*CRTLEN)	/* jump at beginning of crtmac.s */
! 
! #ifndef RELLEN
  #define	RELLEN	12		/* length of relocation table in crt*.s */
  #endif
  
***************
*** 64,69
  #define	CRTLEN	10		/* length of relocation table in crtmac.s */
  #define	RELMAGIC 0x6034		/* jump at beginning of crt*.s */
  #define	RELLEN	12		/* length of relocation table in crt*.s */
  
  #define	NRESCOMP 50		/* max number of resources per compile */
  

--- 73,79 -----
  
  #ifndef RELLEN
  #define	RELLEN	12		/* length of relocation table in crt*.s */
+ #endif
  
  #define	RELMAGIC (0x6004+4*RELLEN)	/* jump at beginning of crt*.s */
  
***************
*** 65,70
  #define	RELMAGIC 0x6034		/* jump at beginning of crt*.s */
  #define	RELLEN	12		/* length of relocation table in crt*.s */
  
  #define	NRESCOMP 50		/* max number of resources per compile */
  
  struct	rescomp {		/* resource being compiled */

--- 75,82 -----
  #define	RELLEN	12		/* length of relocation table in crt*.s */
  #endif
  
+ #define	RELMAGIC (0x6004+4*RELLEN)	/* jump at beginning of crt*.s */
+ 
  #define	NRESCOMP 50		/* max number of resources per compile */
  
  struct	rescomp {		/* resource being compiled */
***************
*** 137,143
  	"CDEF",	handproc,
  	"MDEF",	handproc,
  	"WDEF",	handproc,
! 	"POST", handpost,
  	"FKEY", handproc,
  	"PROC", handproc,
  	0,	0

--- 149,155 -----
  	"CDEF",	handproc,
  	"MDEF",	handproc,
  	"WDEF",	handproc,
! 	"POST", handstrl,
  	"FKEY", handproc,
  	"PROC", handproc,
  	0,	0
***************
*** 493,537
  	int i;
  
   	for (datap++, len = 0 ; *sp ; len++) {
!  		if ((c = *sp++) != '\\') {
!  			*datap++ = c;
!  			continue;
!  		}
!  		switch (c = *sp++) {
!  		default:
!  			*datap++ = c;
!  			continue;
!  		case 'b':
!  			*datap++ = '\b';
!  			continue;
!  		case 'f':
!  			*datap++ = '\f';
!  			continue;
!  		case 'n':
!  			*datap++ = '\n';
!  			continue;
!  		case 'r':
!  			*datap++ = '\r';
!  			continue;
!  		case 't':
!  			*datap++ = '\t';
!  			continue;
!  		case '0':
!  		case '1':
!  		case '2':
!  		case '3':
!  		case '4':
!  		case '5':
!  		case '6':
!  		case '7':		/* octal constant */
!  			*datap = c - '0';		/* stop after      */
! 			for (i = 2 ; i <= 3 ; i++) {	/* three digits    */
! 				if ((c = *sp) < '0' || c > '7')	   /* or   */
! 					break;		/* non-octal digit */
! 				c -= '0';
! 				*datap <<= 3;
! 				*datap |= (c & 0x7);
! 				*++sp;
  			}
  			datap++;
   			break;

--- 505,518 -----
  	int i;
  
   	for (datap++, len = 0 ; *sp ; len++) {
! 		/* \XX where XX are two hex digits emits ASCII code XX;
! 		   \\ emits a single backslash;
! 		   \ followed by garbage emits the \ and the garbage. */
! 		if (*sp == '\\') {
! 			if (ishex(sp[1]) && ishex(sp[2])) {
! 				*datap++ = tohex(sp[1])*16 + tohex(sp[2]);
! 				sp += 3;
! 				continue;
  			}
  			/* else */ if (sp[1] == '\\')
  				++sp;
***************
*** 533,557
  				*datap |= (c & 0x7);
  				*++sp;
  			}
! 			datap++;
!  			break;
!  		case 'x':		/* hex constant */
!  			*datap = 0;
! 			for (i = 1 ; i <= 2; i++) {	/* stop after two */
! 				c = *sp;		/* digits or      */
!  				if (!isxdigit (c))	/* non-hex digit  */
! 					break;
! 				else if (isdigit(c))
! 					c -= '0';
! 				else
! 					c -= isupper(c) ? 'A' - 10 : 'a' - 10;
!  				*datap <<= 4;
!  				*datap |= (c & 0xf);
! 				*++sp;
!  			}
!  			datap++;
!  			break;
!  		}
   	}
  	*cp = len;
  	len++;

--- 514,523 -----
  				sp += 3;
  				continue;
  			}
! 			/* else */ if (sp[1] == '\\')
! 				++sp;
! 		}
! 		*datap++ = *sp++;
   	}
  	*cp = len;
  	len++;
***************
*** 562,567
  	return (len);
  }
  
  
  /*
   * Scan next number from line and return it.

--- 528,539 -----
  	return (len);
  }
  
+ ishex(c)
+ 	char c;
+ {
+ 	return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' ||
+ 	       c >= 'a' && c <= 'f';
+ }
  
  tohex(c)
  	char c;
***************
*** 563,568
  }
  
  
  /*
   * Scan next number from line and return it.
   */

--- 535,549 -----
  	       c >= 'a' && c <= 'f';
  }
  
+ tohex(c)
+ 	char c;
+ {
+ 	if (c >= 'a') return c - 'a' + 10;
+ 	if (c >= 'A') return c - 'A' + 10;
+ 	return c - '0';
+ }
+ 
+ 
  /*
   * Scan next number from line and return it.
   */
***************
*** 1329,1358
  	/* end of items */
  	*datap++ = 0;
  	*flagsp = htonl(flags);
- }
- 
- 
- /*
-  * Handle postscript strings ("Laser Prep" file).
-  *  First line is number of lines that follow.
-  *  Each following line is a string.
-  */
- handpost()
- {
- 	int count;
- 	int nbytes;
- 
- 	if (getline() == 0)	/* get string count */
- 		abort("missing string in POST");
- 	sscanf(lp, "%d", &count);
- 	*datap++ = count >> 8;
- 	*datap++ = count & 0xff;
- 	while (count--) {
- 		getline();
- 		datastring(lp,0);
- 	}
- 	/* Pad whole resource if odd byte count (is this necessary?) */
- 	nbytes = datap - data;
- 	if (nbytes & 1)
- 		*datap++ = '\0';
  }

--- 1310,1313 -----
  	/* end of items */
  	*datap++ = 0;
  	*flagsp = htonl(flags);
  }

guido@boring.UUCP (07/12/85)

Here are the manual updates for my rmaker patches.  Use "patch" in the
directory where you unpacked the posting by Brian Powell.
	Guido van Rossum, CWI, Amsterdam (guido@mcvax.UUCP)

*** posted-rmaker.doc	Fri Jul 12 09:44:24 1985
--- rmaker.doc	Fri Jul 12 10:00:25 1985
***************
*** 2,7
  FORMAT OF THE SUMACC RESOURCE COMPILER INPUT FILE
  	Modified 07/01/85 by Brian Powell	Adapt for SUMacC
  		 07/06/85    Brian Powell	mention backslash escapes
  _______________________________________________________________________________
  
  You'll need to create a resource file for your application.  This is

--- 2,8 -----
  FORMAT OF THE SUMACC RESOURCE COMPILER INPUT FILE
  	Modified 07/01/85 by Brian Powell	Adapt for SUMacC
  		 07/06/85    Brian Powell	mention backslash escapes
+ 		 07/12/85    Guido van Rossum	change escapes and POST
  _______________________________________________________________________________
  
  You'll need to create a resource file for your application.  This is
***************
*** 98,105
  
     - The Type statements must be grouped by type in the input file.
  
!    - In general, all strings may include backslash escape characters.
! (\b, \f, \n, \r, \t, \377, \x7a, etc.)
  
  
  Type WIND			Window template

--- 99,106 -----
  
     - The Type statements must be grouped by type in the input file.
  
!    - In general, all strings may include backslash escape sequences
! (\XX, where XX are two hexadecimal digits).
  
  
  Type WIND			Window template
***************
*** 221,227
  				[ SEE NOTE 3 BELOW ]
  Type POST			Postscript resource
    ,1				Resource ID
- 4				Number of strings
  This is string one		The strings...
  This is string two
  Third string

--- 222,227 -----
  				[ SEE NOTE 3 BELOW ]
  Type POST			Postscript resource
    ,1				Resource ID
  This is string one		The strings...
  This is string two
  Third string
***************
*** 226,231
  This is string two
  Third string
  Bench warmer			Apple's joke, not mine.
  
  Notes:
  

--- 226,232 -----
  This is string two
  Third string
  Bench warmer			Apple's joke, not mine.
+ 				Blank line required following POST
  
  Notes:
  
*** posted-rmaker.1	Fri Jul 12 09:46:35 1985
--- rmaker.1	Fri Jul 12 09:56:19 1985
***************
*** 29,37
  The optional \-d (debug) switch will list out in hex the contents
  of all resources matching that four letter type.
  .PP
! The usual backslash escape sequences are allowed in strings (\\[bfnrt] and
! \\ooo where the "o"'s are octal digits.)  Also, \\xhh is allowed where "h" is
! a hexadecimal digit.
  .SH SEE ALSO
  "Putting Together a Macintosh Application"
  (note:  this manual is no longer part of Inside Macintosh)

--- 29,36 -----
  The optional \-d (debug) switch will list out in hex the contents
  of all resources matching that four letter type.
  .PP
! The hexadecimal escape sequences that are (were once) described in Inside Mac
! are allowed in strings (\\XX where XX are two hexadecimal digits).
  .SH SEE ALSO
  "Putting Together a Macintosh Application"
  (note:  this manual is no longer part of Inside Macintosh)
***************
*** 49,54
  to simulate unimplemented types.  GNRL would be even better for this
  (if someone would only implement it!)
  .PP
  Rmaker input has no "standard" format.  Where possible, the format of the
  Lisa Rmaker has been used.  In the case of STR#, the format is different
  than that of the Mac Rmaker.  See "Format of the SUMacC Resource Compiler

--- 48,57 -----
  to simulate unimplemented types.  GNRL would be even better for this
  (if someone would only implement it!)
  .PP
+ The "nonstandard menu" feature of the Lisa Rmaker is not supported.
+ .PP
+ While initial spaces are skipped on most input lines, tabs are not!
+ .PP
  Rmaker input has no "standard" format.  Where possible, the format of the
  Lisa Rmaker has been used.  In the case of STR#, the format is different
  than that of the Mac Rmaker.  See "Format of the SUMacC Resource Compiler
***************
*** 53,58
  Lisa Rmaker has been used.  In the case of STR#, the format is different
  than that of the Mac Rmaker.  See "Format of the SUMacC Resource Compiler
  Input File."
  .PP
  If you get the message "impossible relocation", it usually means your
  b.out had some undefined external references;  check the error output

--- 56,62 -----
  Lisa Rmaker has been used.  In the case of STR#, the format is different
  than that of the Mac Rmaker.  See "Format of the SUMacC Resource Compiler
  Input File."
+ (POST is treated the same as STR#.)
  .PP
  If you get the message "impossible relocation", it usually means your
  b.out had some undefined external references;  check the error output