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