/* * CKMKE3.C * * Key-Config is a keyboard configurator program for use with Columbia's * MacKermit. * * Bill Schilit, April 1985 * * * Copyright (C) 1985, Trustees of Columbia University in the City of * New York. Permission is granted to any individual or institution to * use, copy, or redistribute this software so long as it is not sold * for profit, provided this copyright notice is retained. * */ #include "mac/quickdraw.h" /* order does matter */ #include "mac/osintf.h" /* on these... */ #include "mac/toolintf.h" #include "mac/packintf.h" #include "ckmkey.h" #include "ckmkkc.h" #include WindowPtr setfw; #define setfwd ((DialogRecord *) setfw) /* coerced to point to dlg */ BOOL dirty = FALSE; /* if display need saving */ int oldtop = -1; /* previous topline */ int fdisp[] = {SETFD_ST1,SETFD_ST2,SETFD_ST3,SETFD_ST4,SETFD_ST5,0}; int fvals[] = {SETFD_ET1,SETFD_ET2,SETFD_ET3,SETFD_ET4,SETFD_ET5,0}; #define NFDISPS 5 /* number of functions displayed */ char *fkeys[NFKEYS]; /* unparsed function defs */ char TEXT[] = {"TEXT"}; /* long aligned "text" */ char APPL[] = {"APPL"}; /* long aligned "appl" */ /* octout - translate number to backslash followed by 3 octal digits */ char *octout(s,n) char *s; { *s++ = '\\'; /* do backslash */ *s++ = ((n >> 6) & 07) + '0'; /* first digit */ *s++ = ((n >> 3) & 07) + '0'; /* second digit */ *s++ = (n & 07) + '0'; /* yes, you guessed it, third digit */ return(s); SYM(OCTOUT); } #define isnoctal(c) (c < '0' || c > '7') char *octin(s,val) int *val; char *s; { *val = ((s[0] - '0') << 6 | (s[1] - '0') << 3 | (s[2] - '0')); if (isnoctal(s[0]) || isnoctal(s[1]) || isnoctal(s[2])) *val = 0xff+1; /* indicate error */ return(&s[3]); /* return updated ptr */ } /* updatefkey - given item number and function key index, update fkey * array from edit text item. * */ updatefkey(rid,fkn) { char etbuf[256]; GetIText(gethdl(rid,setfw),etbuf); /* get the text */ DisposPtr(fkeys[fkn]); /* dispose old ptr */ fkeys[fkn] = NewPtr(strlen(etbuf)+1); /* assign storage */ strcpy(fkeys[fkn],etbuf); /* copy it in */ return; SYM(GETFKEYS); } /* updatefdisp - Update the function key strings on the screen and in the * fkeys array. * */ int oldself,oldsels,oldsele; updatefdisp(newtop) { char nambuf[10]; int i,t; /* check for the selection range... is it in our window? If so then */ /* update the values we remember - the function number, and the range. */ if ((t = setfwd->editField+1) != SETFD_ETI) { for (i=0; fvals[i] != 0; i++) /* locate by ID */ if (fvals[i] == t) /* found it? */ oldself = oldtop+i; /* yes, set selected fcn number */ oldsels = (*setfwd->textH)->selStart; /* set selection start */ oldsele = (*setfwd->textH)->selEnd; /* and selection end */ } SelIText(setfw,SETFD_ETI,0,0); /* make selection be invisible, */ /* prevents odd looking updates */ /* during scrolling */ /* if "dirty" is set then update fkeys array from edit text items */ if (dirty) /* if something may have changed */ for (i=0; fvals[i] != 0; i++) /* then loop saving edit text items */ updatefkey(fvals[i],oldtop+i); /* into fkey array */ dirty = FALSE; /* nothing dirty any longer */ if (oldtop != newtop) /* if some work needs to be done */ for (i=0, t = newtop; fdisp[i] != 0; i++) { nambuf[0] = 'F'; NumToString(t,&nambuf[1]); SetIText(gethdl(fdisp[i],setfw),nambuf); SetIText(gethdl(fvals[i],setfw),fkeys[t++]); } /* * Manage the selection range: let the edit text selection range move * around, sticking with the function definition it was being used with. * If the function is not visible, then use already selected invisible item. */ if (oldself >= newtop && oldself < newtop+NFDISPS) SelIText(setfw,fvals[oldself-newtop],oldsels,oldsele); oldtop = newtop; /* remember for next time */ return; SYM(UPDATENAMES) } pagescroll(part,ctl) ControlHandle ctl; { Point p; int amount = (part == inPageUp) ? -NFDISPS : NFDISPS; do { GetMouse(&p); if (TestControl(ctl,&p) != part) continue; SetCtlValue(ctl,GetCtlValue(ctl)+amount); updatefdisp(GetCtlValue(ctl)); } while (StillDown()); return; SYM(PAGESCROLL); } scrollbtn() { struct { /* args passed from Pascal */ short part; /* TrackControl in stack order */ ControlHandle ctlh; } args; int delta; getpargs(&args,sizeof args); switch (args.part) { case inUpButton: delta = -1; break; case inDownButton: delta = 1; break; default: return; } SetCtlValue(args.ctlh,GetCtlValue(args.ctlh)+delta); updatefdisp(GetCtlValue(args.ctlh)); return; SYM(SCROLLBTN); } setfkfilter() { struct { /* pascal args for filter procedure */ short *item; /* (in stack order) */ EventRecord *evt; DialogPtr dlg; } args; BOOL *fcnret; Point lpt; ControlHandle ctlhdl; int part; fcnret = (BOOL *) getpargs(&args,sizeof args); *fcnret = FALSE; /* assume we don't want to handle */ if (args.evt->what == keyDown) /* key down? */ { if (((DialogRecord *) args.dlg)->editField == SETFD_ETI-1) { /* typing to invisible selection? */ SysBeep(1); /* yes... beep them */ *fcnret = TRUE; /* do not let modal do anything */ *args.item = SETFD_ETI; /* a no-op */ return; /* and return now */ } dirty = TRUE; /* else things may change */ } if (args.evt->what != mouseDown) /* is this mouse down? */ return; /* no, we don't do anything */ lpt = args.evt->where; /* copy the point */ GlobalToLocal(&lpt); /* convert to local coords */ part = FindControl(&lpt,args.dlg,&ctlhdl); /* find a control if any */ switch (part) { case inUpButton: case inDownButton: TrackControl(ctlhdl,&lpt,scrollbtn); break; case inPageUp: case inPageDown: pagescroll(part,ctlhdl); break; case inThumb: TrackControl(ctlhdl,&lpt,NILPROC); updatefdisp(GetCtlValue(ctlhdl)); break; default: return; } *fcnret = TRUE; /* we handled it */ return; SYM(SETFKFILTER) } /* getindstr - given an indirect (or is it indexed) string and integer * n, return the pointer to the Nth substring. * * Indirect strings have the count of substrings in the first byte and * each string follows with a length byte and a body. * * Substrings are referenced by 1..N * */ char *getindstr(indstr,n) char *indstr; { register char *ip = indstr; int i; if (n > *ip++) /* too large? */ return(NILPTR); /* yes, nothing there */ for (i=1; i < n; i++) /* scan until we hit the Nth */ ip += (*ip)+1; /* move to next substring */ return(ip); /* return ptr to it */ SYM(GETINDSTR); } /* chkcodestr - checks a backslash coded string for correctness. */ chkcodestr(s,ber,eer) char *s; int *ber,*eer; /* start and end of error */ { char *pi,*pix; int val; for (pi = s; *pi != NULL; ) /* check the string */ if (*pi++ == '\\') { /* found something? */ pix = octin(pi,&val); /* yes, fetch number */ if (val > 0xff) { /* error? */ *ber = pi-s-1; /* start of error */ *eer = pi-s+3; /* end of error */ return(FALSE); /* failed */ } pi = pix; /* update ptr */ } /* and loop */ return(TRUE); } /* encodestr - encode backslash string, return NewPtr string. */ char *encodestr(s) char *s; /* actually pascal string */ { char buf[256], /* big buf */ *dp = buf, /* destination ptr */ *sp = s, /* source ptr */ *newp; int len,olen; len = (sp != NILPTR) ? *sp++ : 0; /* decide on length */ for (dp = buf, olen = 0; /* deposit into our buffer */ len > 0 && olen < 64; len--) { /* for each byte in string */ olen++; /* one more in destination */ if (isprint(*sp) || *sp == ' ') /* is it printable or space? */ *dp++ = *sp++; /* yes, just copy it */ else { dp = octout(dp,*sp++); /* else use backslash and digits */ olen += 3; /* account */ } } *dp++ = 0; /* tie off */ newp = (char *) NewPtr(olen+2); /* allocate storage */ strcpy(newp,buf); /* copy definition */ return(newp); /* return it */ } /* decodestr - decode backslash string in place, make a pascal string * returns size. */ decodestr(s) char *s; { char *pi,*po; int li,lo,val; c2pstr(pi = s); /* convert to pascal string */ for (li = *pi++, lo=0, po=pi; /* init lengths, output ptr */ li > 0; li--) { /* do the entire string */ if (*pi == '\\') { /* special character? */ pi = octin(++pi,&val); /* yes fetch value (checked already) */ *po++ = val; /* store it */ li -= 3; /* account for digit bytes */ } else *po++ = *pi++; /* else just copy */ lo++; /* count up output chars */ } s[0] = lo; /* set size */ return(lo); /* return size */ } /* bldindstr - build an indexed string from fkeys array and return * a relocatable handle to the new indexed string. * * An indexed string is contains the count of substrings in the first * byte and each string in pascal form (length byte then body) follows. * * This routine takes the contents of fkeys and translate the backslash * numbers to single bytes. This conversion is done in place, as we * know the result will always be leq in size. Since the final form * requires pascal form strings, and since the backslash might result * in imbedded nulls, we first convert fkeys to pascal strings. * * The count of substrings in the indexed string will probably be less * than NFKEYS (the number of definable function keys) since the last * batch of empty definitions need not be included. * * The resulting indexed string is allocated from the heap, it is * set non-purgeable, but it is relocatable. A handle to this * uber-string is returned. * * N.B. Don't try using fkeys after this routine has been called * since it now contains decoded strings, not backslash strings. * */ Handle bldindstr() { int i,l, hifk = 0, /* number of idx strings */ tsize = 0; /* total size of these */ char *fki,*fko; Handle rp; for (i=0; i < NFKEYS; i++) { /* modify strings in place */ tsize += (l = decodestr(fkeys[i])); /* decode the string in place */ /* accumulate size */ if (l > 0) hifk = i; /* remember highest with body */ } tsize += hifk+1; /* hifk is how many, add size bytes */ rp = NewHandle(tsize+1); /* get stg + count byte */ HNoPurge(rp); /* don't remove us */ fko = *rp; /* de-reference handle */ *fko++ = hifk+1; /* store count of substrings */ for (i=0; i <= hifk; i++) /* now store them */ { fki = fkeys[i]; /* handle on input string */ BlockMove(fki,fko,fki[0]+1); /* move it to output */ fko += fki[0]+1; /* increment output ptr */ } return(rp); /* return ptr */ SYM(BLDINDSTR); /* for debugging */ } /* savefkeys - Check & save the fkeys array. * * The fkeys array is checked for correctnes (backslash badness). If * something is wrong we will put the bad definition in a visible edit * text item in the display (if already visible no movement occurs) * select the bogus region, beep and return FALSE. * * If no badness is detected we release the current kset function def * handle, call buildidx to create a new one, store it, and return TRUE. * */ BOOL savefkeys(sctl) ControlHandle sctl; { int n,ctlv,sbeg,send; updatefdisp(GetCtlValue(sctl)); /* make sure fkeys updated */ for (n=0; n < NFKEYS; n++) { /* for all keys, check correctness */ if (!chkcodestr(fkeys[n],&sbeg,&send)) { /* check out? */ ctlv = GetCtlValue(sctl); /* no, get control setting */ if (n > ctlv+NFDISPS || n < ctlv) /* if N is not in our window */ ctlv = n; /* need to reposition */ SetCtlValue(sctl,ctlv); /* set it */ updatefdisp(ctlv); /* update the function display */ SelIText(setfw, /* selects zeroth or 1st, 2nd */ fvals[(n - ctlv)], /* edit item according to top */ sbeg,send); /* region of badness */ SysBeep(2); /* beep them */ return(FALSE); /* and indicate error */ } } DisposHandle((*kshdl)->fcnshdl); /* return old handle */ (*kshdl)->fcnshdl = bldindstr(); /* build new one */ return(TRUE); SYM(SAVEFKEYS); } /* initfkeys - given a ptr to the indexed string for function keys * decode this string into the fkeys array. * * Decoding is simply converting non-printable characters to backslash * followed by 3 octal digits. Storage for encoded fkeys is allocated * in this routine, no modification is made to the indexed string. * */ initfkeys(indstr) char *indstr; { char *sp; int n; for (n=0; n < NFKEYS; n++) { /* do all of functions */ sp = getindstr(indstr,n+1); /* get ptr to indexed string */ fkeys[n] = encodestr(sp); /* encode it */ } return; SYM(INITFKEYS); } /* disposfkeys() - return storage used by fkeys array. * */ disposfkeys() { int i; for (i=0; i < NFKEYS; i++) if (fkeys[i] != NILPTR) { DisposPtr((Ptr) fkeys[i]); /* a comment: return it */ fkeys[i] = NILPTR; } } setfkeydialog() { int itemhit; ControlHandle scrollctl; Handle fkhdl; setfw = GetNewDialog(DLOG_SETF,NILPTR,(WindowPtr) -1); scrollctl = getctlhdl(SETFD_SCR,setfw); SetCtlMax(scrollctl,NFKEYS-NFDISPS); /* make value */ SetPort(setfw); /* make globaltolocal work right */ dirty = FALSE; /* nothing is dirty */ oldtop = -1; /* previous topline */ fkhdl = (*kshdl)->fcnshdl; /* handle to function defs */ HLock(fkhdl); /* lock default key defs */ initfkeys(*fkhdl); /* de-ref and init fkeys array */ HUnlock(fkhdl); /* unlock the handle */ updatefdisp(0); ShowWindow(setfw); for (;;) { ModalDialog(setfkfilter,&itemhit); switch (itemhit) { case SETFD_SCR: break; case SETFD_SET: if (!savefkeys(scrollctl)) /* try to save it... */ break; /* if fail, continue dialog */ modified = TRUE; /* things have changed */ case SETFD_QUIT: /* else fall into quit case */ DisposDialog(setfw); disposfkeys(); /* dispose fkeys array */ return; } } SYM(SETFKDIALOG) } /* hexout - translate the byte to two octal digits. */ char hdigits[] = "0123456789abcdef"; hexout(dp,hc) char hc,*dp; { *dp++ = hdigits[(hc >> 4) & 0x0f]; *dp++ = hdigits[hc & 0x0f]; } /* writehexhdl - given a file number, a handle, output the handle * in hex to the file. * */ writehexhdl(okflg,f,hdl) int *okflg; Handle hdl; { char *s,lb[100]; /* line buffer */ PLONG oc,l; if (!*okflg) /* error occured */ return; /* just return */ if ((l = GetHandleSize(hdl)) == 0) /* find size, anything to do? */ return; /* nope... */ HLock(hdl); /* lock the handle */ s = *hdl; /* de-reference */ oc = 0; /* output count */ for (; l > 0; l--) { /* for each character */ hexout(&lb[oc],*s++); /* put in digits */ oc += 2; /* account */ if (oc > 75 || l == 1) { /* new line needed? */ lb[oc++] = '\n'; /* yes, add nl character */ if (!(*okflg = syserr(FSWrite(f,&oc,lb)))) /* output it */ return; /* couldn't */ oc = 0; /* init count */ } else lb[oc++] = ' '; /* else format */ } HUnlock(hdl); /* unlock... */ return(TRUE); /* done ok */ SYM(WRITEHEXH); /* for debugger */ } writestr(okflg,f,s) int *okflg; char *s; { PLONG l; int err; if (!*okflg) /* error occured, so nop */ return; l = strlen(s); *okflg = syserr(FSWrite(f,&l,s)); /* set to FALSE if error */ } writetype(okflg,f,type) int *okflg; char *type; { char numbuf[10]; writestr(okflg,f,"\nType "); writestr(okflg,f,type); writestr(okflg,f," = HEXA\n ,"); NumToString(KSVER,numbuf); writestr(okflg,f,numbuf); writestr(okflg,f,"\n"); } /* Decompile the current file */ decomdialog(name) char *name; { SFReply sfr; char defnam[256]; Point where; int fnum,err,okflg; strcpy(defnam,name); /* copy name */ strcat(defnam,"R"); /* here is new form... */ SetPt(&where,75,100); /* some place */ SFPutFile(&where,"Save decompiled information in:", defnam,NILPROC,&sfr); if (!sfr.good) /* really want to? */ return; /* nope... */ err = FSDelete(isapstr(sfr.fName),sfr.vRefNum); /* delete old file */ if (err != noErr && err != fnfErr) { syserr(err); /* do message */ return; /* and return */ } if (syserr(Create(isapstr(sfr.fName),sfr.vRefNum,APPL,TEXT))) return; /* forget it */ if (syserr(FSOpen(isapstr(sfr.fName),sfr.vRefNum,&fnum))) return; /* forget it */ okflg = TRUE; /* everything is ok... */ writestr(&okflg,fnum,"* Decompiled information from "); writestr(&okflg,fnum,name); writestr(&okflg,fnum,"\n\n"); writetype(&okflg,fnum,"FSET"); writehexhdl(&okflg,fnum,(*kshdl)->fcnshdl); /* write out functions */ writestr(&okflg,fnum,"\n"); writetype(&okflg,fnum,"MSET"); writehexhdl(&okflg,fnum,(*kshdl)->metahdl); /* write out meta string */ writestr(&okflg,fnum,"\n"); writetype(&okflg,fnum,"KSET"); writehexhdl(&okflg,fnum,(Handle) kshdl); /* write out key set */ writestr(&okflg,fnum,"\n"); if (syserr(FSClose(fnum))) /* close it */ return; /* failed */ return; SYM(DECOMDIALOG); }