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);
}