[comp.sys.mac.programmer] MPW tool to patch resources or files

zben@ni.umd.edu (Ben Cranston) (04/27/91)

There were some simple example MPW tools in this month's MacTutor.  I had
never realized it was that easy to write a tool!  Here is a tool that can
be used to "patch" a resource or a data fork with either inline hex data,
the contents of another resource, or the contents of another data fork.
For example, the "command-F" patches for LaserWriter 6.0.1 can be done
with this command:

 Patch LaserWriter PDEF(125) <command-d>
    $c78:51 $c7c:50 $c84:50 $c8c:51 $c9a:50 $e9e:6038 $1860:60

Some of the diagnostics are a bit wrong but I think a diagnostic is given
for every error case.  Also there is some debug printout you can take out
when you trust it.  There is a neat hex dump routine buried in here too!


/* MPW tool to "Patch" data or resource fork of file
 *
 * For usage notes see below...
 *
 * Ben Cranston <zben@ni.umd.edu>
 * University of Maryland at College Park
 * 910419
 */

#include <CType.h>
#include <ErrMgr.h>
#include <Files.h>
#include <Memory.h>
#include <Resources.h>
#include <StdIO.h>
#include <String.h>

#define P(x) fprintf(stderr,"%s%s\n",progname,x);
#define Q(x) fprintf(stderr,"%s\n",x);

char	*progname;

usage()
{
P(": Usage:							")
P(" file offset:hexdata						")
P(" file offset infile inoffset:length				")
P(" file offset infile resource					")
Q("");
P(" file resource offset:hexdata				")
P(" file resource offset infile inoffset:length			")
P(" file resource offset infile resource			")
Q("");
Q("where resource is  restype(resnum)  or  restype@resname	")
}

/*
 * Test for valid hexadecimal digit.
 */

#define ishex(x) (isascii(x)&&isxdigit(x))

/*
 * Integer value (0-15) for hexadecimal digit.
 */

#define toint(x) ((x)<='9'?(x)-'0':(x)<='F'?(x)-'A'+10:(x)-'a'+10)

/*
 * Print out system string for a particular error code.
 */

syserr(short errcode)
{
	char	errmsg[256];

	GetSysErrText(errcode,errmsg);
	fprintf(stderr,"# %s\n",errmsg);
}

/*
 * Diagnose syntax error on call to this tool.
 */

syntax(char *s)
{
	fprintf(stderr,"%s - %s\n",progname,s);
	exit(1);
}

/*
 * Diagnose file access problems.
 */

cant(char *diag, char *fname, short errcode)
{
	fprintf(stderr,"# %s - Can't %s : %P\n",progname,diag,fname);
	syserr(errcode);
	exit(2);
}

/*
 * Show arguments to this program.
 */

dumpargs(int argc, char **argv)
{
	short i;

	fprintf(stderr,"argc = %d\n",argc);
	for (i=0 ; i<argc; i++)
		fprintf(stderr,"argv[%d] = \"%s\"\n",i,argv[i]);
}

/*
 * Dump area in hexadecimal and readable formats.
 */

hexdump(int num, char *buf)
{
        short rx,cx;
        char chr;
        char vr[17];

        for ( rx=0 ; rx<num ; rx+=16 ) {
        fprintf(stderr,"%03X  ",rx);
                for ( cx=0 ; cx<16 ; cx++ ) {
                        if ( (rx+cx) < num ) {
                                chr = buf[rx+cx];
                                fprintf(stderr,"%02X ",chr&0xFF);
                                vr[cx] = (isascii(chr)&&isprint(chr))?chr:'.';
                        } else {
                                fprintf(stderr,"   ");
                                vr[cx] = ' ';
                        }
                }
                vr[16] = 0;
                fprintf(stderr," %s\n",vr);
        }
}

/*
 * Scan and convert integer, either decimal or $hex.
 */

int getnum(char *s, char t)
{
	int	acc = 0;
	short	sign = 1;

	if ('$' == (*s)) {
		s++;
		while ( ishex(*s) ) {
			acc = (acc<<4) | toint(*s);
			s++;
		}
	} else {
		if ('-' == (*s)) {
			s++;
			sign = -1;
		}
		while ( (isascii(*s)&&isdigit(*s)) ) {
			acc = 10*acc + (*s) - '0';
			s++;
		}
		acc = sign*acc;
	}

	if (t!=0) {
		if (t!=(*s))
			syntax("missing field terminator");
		else
			s++;
	}

	if (*s)
		syntax("bad character in integer field");
	return(acc);
}

/*
 * Scan a resource designator and read in the designaged resource.
 * This is called both to get resource data for a patch source
 * and to get in a resource that is itself to be patched.
 *
 * Formats are:
 *	PDEF(125)
 *	$50444546(125)		# same, but restype specified in hex
 *	PDEF@resourcename	# resource designated by name rather than number
 */

short
getres(char *fname, char *sfield, short perms, short *rfnum, Handle *rshand)
{
	char	*sp;
	short	which, rsnum, status;
	OSType	rstype;
	char	buffer[6], rsname[256];

	if (sp = strchr(sfield,'(') ) {
		which = 1;
		rsnum = getnum(sp+1,')');
	} else if (sp = strchr(sfield,'@') ) {
		which = 0;
		rsname[0] = strlen(sp+1);
		BlockMove(sp+1,&rsname[1],rsname[0]);
	} else
		return(0);

	(*sp) = 0;
	if ('$' == (*sfield) )
		rstype = getnum(sfield,0);
	else if (4 >= (status = sp-sfield) ) {
		rstype = (OSType) '    ';
		BlockMove(sfield,(char *)&rstype,status);
	} else
		syntax("bad resource type field");

	SetResLoad(false);
	(*rfnum) = OpenRFPerm(fname,0,perms);
	SetResLoad(true);
	if (0 > (*rfnum) )
		cant("open (resource fork of) file",fname,ResError());

	if (which)
		(*rshand) = Get1Resource(rstype,rsnum);
	else
		(*rshand) = Get1NamedResource(rstype,rsname);

	if (0 == (*rshand) ) {
		*(OSType *)&buffer = rstype;
		buffer[4] = 0;
		if (which)
			fprintf(stderr,"# %s - Can't load resource : %P %s %d\n",
				progname,fname,buffer,rsnum);
		else
			fprintf(stderr,"# %s - Can't load resource : %P %s %P\n",
				progname,fname,buffer,rsname);
		if (0 == (status=ResError()) )
			fprintf(stderr,"# No ResError worth mentioning...\n");
		else
			syserr(status);
		exit(2);
	}

	return(1);
}

main(int argc, char **argv)
{
	short	rfnum, rrfnum, status;
	char	*sp;
	int	length, offset, roffset, xflen;
	char	fname[256];
	char	bigbuf[1024];
	Handle	rshand, rrshand;

/*	dumpargs(argc,argv);	*/
	progname = argv[0];
	argc--, argv++;
	if (argc < 2) {
		usage();
		exit(1);
	}
	
	fname[0] = strlen(argv[0]);
	BlockMove(argv[0],&fname[1],fname[0]);
/*
 * Open file (and possibly resource) to be patched
 */
	if ( getres(fname,argv[1],fsRdWrPerm,&rfnum,&rshand) )
		argc--, argv++;
	else {
		if ( 0 > (status = FSOpen(fname,0,&rfnum)) )
			cant("open (data fork of) file",fname,status);
		rshand = (Handle) 0;
	}
	argc--, argv++;
	
	do {
/*
 * Get patch
 */
		if (0 > argc)
			syntax("missing patch field!");
		if (sp = strchr(argv[0],':') ) {
			*sp = 0;
			offset = getnum(argv[0],0);
			length = 0;
			while ( *(sp+1) ) {
				if (ishex(*(sp+1)) && ishex(*(sp+2))) {
					bigbuf[length++] =
						(toint(*(sp+1))<<4) + toint(*(sp+2));
					sp += 2;
				} else
					syntax("error in hex input data");
			}
		} else {
			offset = getnum(argv[0],0);
			argc--, argv++;
			if (0 > argc)
				syntax("missing patch file!");
			fname[0] = strlen(argv[0]);
			BlockMove(argv[0],&fname[1],fname[0]);
			if ( getres(fname,argv[1],fsRdPerm,&rrfnum,&rrshand) ) {
				length = GetHandleSize(rrshand);
				if ( sizeof(bigbuf) < length )
					syntax("resource too big!!!");
				BlockMove(*rrshand,bigbuf,length);
				CloseResFile(rrfnum);
			} else if (sp = strchr(argv[1],':') ) {
				*sp = 0;
				roffset = getnum(argv[1],0);
				length = getnum(sp+1,0);
				if (0 > (status = FSOpen(fname,0,&rrfnum)) )
					cant("open (data fork of) file",fname,status);
				if (0 > (status = SetFPos(rrfnum,fsFromStart,roffset)) )
					cant("reposition within file",fname,status);
				xflen = length;
				if (0 > (status = FSRead(rrfnum,&xflen,bigbuf)) )
					cant("read (data fork of) file",fname,status);
				if (xflen != length) {
					fprintf(stderr,"data short read %d\n",xflen);
					length = xflen;
				}
				if (0 > (status = FSClose(rrfnum)) )
					cant("close (data fork of) file",fname,status);
			} else
				syntax("missing : in offset:length field");
			argc--, argv++;
		}
		argc--, argv++;

		fprintf(stderr,"offset = %d\n",offset);
		fprintf(stderr,"length = %d\n",length);
		hexdump(length,bigbuf);
/*
 * Apply patch
 */
		if (rshand) {
			if ( (offset+length) > GetHandleSize(rshand) ) {
				SetHandleSize(rshand,offset+length);
				if (noErr != (status=MemError()) ) {
					fprintf(stderr,"# Could not expand handle\n");
					syserr(status);
					exit(2);
				}
			}
			BlockMove(bigbuf,(offset+*rshand),length);
		} else {
			if (0 > (status = SetFPos(rfnum,fsFromStart,offset)) )
				cant("reposition within file",fname,status);
			xflen = length;
			if (0 > (status = FSWrite(rfnum,&xflen,bigbuf)) )
				cant("write to (data fork of) file",fname,status);
			if (xflen != length)
				fprintf(stderr,"data short write %d\n",xflen);
		}

		fprintf(stderr,"at end, argc = %d\n",argc);
	} while (0 < argc);
/*
 * Close file and/or resource fork
 */
	if (rshand) {
		ChangedResource(rshand);
		CloseResFile(rfnum);
	} else {
		if (0 > (status = FSClose(rfnum)) )
			cant("close (data fork of) file",fname,status);
	}

	exit(0);
}