[net.sources.mac] new sumacc rmaker with dynamic relocation

info-mac@uw-beaver (01/25/85)

From: edmoy%ucbopal.CC@UCB-VAX

Here is a new version of rmaker that supports dynamic relocation for drivers
and window, menu and control definition procedures.  It's in shar format.

This dynamic relocation scheme was needed, above the existing self relocating
scheme because desk accessories created by sumacc have a nasty bug in them.
If you run one, the crtdrvr routine will relocate the driver as needed.
If you close this desk accessory and open another, then while the second
is still open, try to reopen the first (sumacc) desk accessory, the Mac
will most certainly bomb.  This is because the desk accessory has been moved
in memory when the second was started up, the the summac desk accessory
can't then relocate itself again.

My method of dynamic relocation causes the size to increase as relocation info
has to be kept around.  But other than that it seems to work.

Also included in this file are a crtdrvr.s and crtwdef.s that implement
the dynamic relocation.

(Currently, the code segment is not dynamically relocating as normally it is
locked in memory when used and when the application is over, the heap is
cleared.)

Edward Moy
Computing Services
University of California
Berkeley, CA  94720

edmoy@ucbopal.APRA
ucbvax!ucbopal!edmoy

----- cut here -------------------------------------- cut here -----------
#! /bin/sh
: This is a shar archive.  Extract with sh, not csh.
echo x - rmaker.c
cat > rmaker.c << '28336!Funky!Stuff!'
/*	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.
 * 01/10/84	Moy	Implement [CMW]DEF.  These and DRVR use dynamic
 *			relocation scheme.
 */

#include <stdio.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(), handxdef();
extern	handdlog(), handalrt(), handditl();
extern	handwind(), handmenu(), handcntl();

struct	typehand {		/* type string to handler table */
	char	th_type[8];	/* e.g. "CODE" */
	int	(*th_handler)(); /* format handler function */
} typehand[] = {
	"STR ",	handstr,
	"HEXA",	handhexa,
	"CODE",	handcode,
	"DRVR",	handdrvr,
	"ALRT", handalrt,
	"DITL", handditl, 
	"DLOG", handdlog,
	"WIND", handwind,
	"MENU", handmenu,
	"CNTL", handcntl,
	"ICON", handhexa,
	"ICN#", handhexa,
	"CURS", handhexa,
	"PAT ", handhexa,
	"CDEF",	handxdef,
	"MDEF",	handxdef,
	"WDEF",	handxdef,
	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");
 			if (strncmp(rcpp->rc_type, "DRVR", 4))
 				*rnp++ = (char) i;
 			else {
 				*rnp++ = (char)(i + 1);
 				*rnp++ = 0;
 			}
 			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);
 	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;

	for (datap++, len = 0 ; *sp ; len++)
		*datap++ = *sp++;
	*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 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;
	}
}


/*
 * Handle device driver (or desk accessory.).
 */

#define	DRVROFF	50	/* offset of longruns in DRVR resource */
#define	DRVRMAGIC

handdrvr()
{
	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("drvr malloc");
	rcp->rc_rel = rcp->rc_data + rcp->rc_datalen;
	if (fread(rcp->rc_data, rcp->rc_datalen, 1, rcp->rc_file) != 1)
		abort("drvr 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+DRVROFF) != htons(RELMAGIC))
		abort("no crtdrvr.s prefix");
	relpnt = rcp->rc_data + DRVROFF + 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,
	 " drvr text %d, data %d, bss %d, reloc %d, longruns %d\n",
	 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 control, menu and window definition procedures.
 */

handxdef()
{
	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("[cmw]def malloc");
	rcp->rc_rel = rcp->rc_data + rcp->rc_datalen;
	if (fread(rcp->rc_data, rcp->rc_datalen, 1, rcp->rc_file) != 1)
		abort("[cmw]def 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) != htons(RELMAGIC))
		abort("no crt[cmw]def.s prefix");
	relpnt = rcp->rc_data + 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,
	 " [cmw]def text %d, data %d, bss %d, reloc %d, longruns %d\n",
	 bh.tsize, bh.dsize, bh.bsize, rcp->rc_rellen, RELLEN - rellen);
}


/*
 * 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);
}
28336!Funky!Stuff!
echo x - crtdrvr.s
cat > crtdrvr.s << '28336!Funky!Stuff!'
|
| 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
28336!Funky!Stuff!
echo x - crtwdef.s
cat > crtwdef.s << '28336!Funky!Stuff!'
|
| crtwdef.s - dynamically relocating C runtime startoff for window
|		definition procedures
|
| Copyright (C) 1984, Stanford Univ. SUMEX project
| May be used but not sold without permission.
|
| history
| 01/10/85	Moy	Created
|

	.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
28336!Funky!Stuff!