home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / EMACSOS2.ZIP / BIND.C next >
C/C++ Source or Header  |  1988-10-05  |  18KB  |  777 lines

  1. /*    This file is for functions having to do with key bindings,
  2.     descriptions, help commands and startup file.
  3.  
  4.     written 11-feb-86 by Daniel Lawrence
  5.                                 */
  6.  
  7. #include    <stdio.h>
  8. #include    "estruct.h"
  9. #include    "etype.h"
  10. #include    "edef.h"
  11. #include    "epath.h"
  12.  
  13. PASCAL NEAR help(f, n)    /* give me some help!!!!
  14.            bring up a fake buffer and read the help file
  15.            into it with view mode            */
  16. {
  17.     register WINDOW *wp;    /* scaning pointer to windows */
  18.     register BUFFER *bp;    /* buffer pointer to help */
  19.     char *fname;        /* ptr to file returned by flook() */
  20.  
  21.     /* first check if we are already here */
  22.     bp = bfind("emacs.hlp", FALSE, BFINVS);
  23.  
  24.     if (bp == NULL) {
  25.         fname = flook(pathname[1], TRUE);
  26.         if (fname == NULL) {
  27.             mlwrite("[Help file is not online]");
  28.             return(FALSE);
  29.         }
  30.     }
  31.  
  32.     /* split the current window to make room for the help stuff */
  33.     if (splitwind(FALSE, 1) == FALSE)
  34.             return(FALSE);
  35.  
  36.     if (bp == NULL) {
  37.         /* and read the stuff in */
  38.         if (getfile(fname, FALSE) == FALSE)
  39.             return(FALSE);
  40.     } else
  41.         swbuffer(bp);
  42.  
  43.     /* make this window in VIEW mode, update all mode lines */
  44.     curwp->w_bufp->b_mode |= MDVIEW;
  45.     curwp->w_bufp->b_flag |= BFINVS;
  46.     wp = wheadp;
  47.     while (wp != NULL) {
  48.         wp->w_flag |= WFMODE;
  49.         wp = wp->w_wndp;
  50.     }
  51.     return(TRUE);
  52. }
  53.  
  54. PASCAL NEAR deskey(f, n)    /* describe the command for a certain key */
  55.  
  56. {
  57.     register int c;        /* key to describe */
  58.     register char *ptr;    /* string pointer to scan output strings */
  59.     char outseq[NSTRING];    /* output buffer for command sequence */
  60.  
  61.     /* prompt the user to type us a key to describe */
  62.     mlwrite(": describe-key ");
  63.  
  64.     /* get the command sequence to describe
  65.        change it to something we can print as well */
  66.     cmdstr(c = getckey(FALSE), &outseq[0]);
  67.  
  68.     /* and dump it out */
  69.     ostring(outseq);
  70.     ostring(" ");
  71.  
  72.     /* find the right ->function */
  73.     if ((ptr = getfname(getbind(c))) == NULL)
  74.         ptr = "Not Bound";
  75.  
  76.     /* output the command sequence */
  77.     ostring(ptr);
  78. }
  79.  
  80. /* bindtokey:    add a new key to the key binding table        */
  81.  
  82. PASCAL NEAR bindtokey(f, n)
  83.  
  84. int f, n;    /* command arguments [IGNORED] */
  85.  
  86. {
  87.     register unsigned int c;/* command key to bind */
  88.     register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
  89.     register KEYTAB *ktp; /* pointer into the command table */
  90.     register int found;    /* matched command flag */
  91.     char outseq[80];    /* output buffer for keystroke sequence */
  92.  
  93.     /* prompt the user to type in a key to bind */
  94.     mlwrite(": bind-to-key ");
  95.  
  96.     /* get the function name to bind it to */
  97.     kfunc = getname();
  98.     if (kfunc == NULL) {
  99.         mlwrite("[No such function]");
  100.         return(FALSE);
  101.     }
  102.     ostring(" ");
  103.  
  104.     /* get the command sequence to bind */
  105.     c = getckey((kfunc == meta) || (kfunc == cex) ||
  106.             (kfunc == unarg) || (kfunc == ctrlg));
  107.  
  108.     /* change it to something we can print as well */
  109.     cmdstr(c, &outseq[0]);
  110.  
  111.     /* and dump it out */
  112.     ostring(outseq);
  113.  
  114.     /* if the function is a unique prefix key */
  115.     if (kfunc == unarg || kfunc == ctrlg) {
  116.  
  117.         /* search for an existing binding for the prefix key */
  118.         ktp = &keytab[0];
  119.         while (ktp->k_fp != NULL) {
  120.             if (ktp->k_fp == kfunc)
  121.                 unbindchar(ktp->k_code);
  122.             ++ktp;
  123.         }
  124.  
  125.         /* reset the appropriate global prefix variable */
  126.         if (kfunc == unarg)
  127.             reptc = c;
  128.         if (kfunc == ctrlg)
  129.             abortc = c;
  130.     }
  131.  
  132.     /* search the table to see if it exists */
  133.     ktp = &keytab[0];
  134.     found = FALSE;
  135.     while (ktp->k_fp != NULL) {
  136.         if (ktp->k_code == c) {
  137.             found = TRUE;
  138.             break;
  139.         }
  140.         ++ktp;
  141.     }
  142.  
  143.     if (found) {    /* it exists, just change it then */
  144.         ktp->k_fp = kfunc;
  145.     } else {    /* otherwise we need to add it to the end */
  146.         /* if we run out of binding room, bitch */
  147.         if (ktp >= &keytab[NBINDS]) {
  148.             mlwrite("Binding table FULL!");
  149.             return(FALSE);
  150.         }
  151.  
  152.         ktp->k_code = c;    /* add keycode */
  153.         ktp->k_fp = kfunc;    /* and the function pointer */
  154.         ++ktp;            /* and make sure the next is null */
  155.         ktp->k_code = 0;
  156.         ktp->k_fp = NULL;
  157.     }
  158.  
  159.     /* if we have rebound the meta key, make the
  160.        search terminator follow it            */
  161.     if (kfunc == meta)
  162.         sterm = c;
  163.  
  164.     return(TRUE);
  165. }
  166.  
  167. /* unbindkey:    delete a key from the key binding table    */
  168.  
  169. PASCAL NEAR unbindkey(f, n)
  170.  
  171. int f, n;    /* command arguments [IGNORED] */
  172.  
  173. {
  174.     register int c;        /* command key to unbind */
  175.     char outseq[80];    /* output buffer for keystroke sequence */
  176.  
  177.     /* prompt the user to type in a key to unbind */
  178.     mlwrite(": unbind-key ");
  179.  
  180.     /* get the command sequence to unbind */
  181.     c = getckey(FALSE);        /* get a command sequence */
  182.  
  183.     /* change it to something we can print as well */
  184.     cmdstr(c, &outseq[0]);
  185.  
  186.     /* and dump it out */
  187.     ostring(outseq);
  188.  
  189.     /* if it isn't bound, bitch */
  190.     if (unbindchar(c) == FALSE) {
  191.         mlwrite("[Key not bound]");
  192.         return(FALSE);
  193.     }
  194.     return(TRUE);
  195. }
  196.  
  197. PASCAL NEAR unbindchar(c)
  198.  
  199. int c;        /* command key to unbind */
  200.  
  201. {
  202.     register KEYTAB *ktp;    /* pointer into the command table */
  203.     register KEYTAB *sktp;    /* saved pointer into the command table */
  204.     register int found;    /* matched command flag */
  205.  
  206.     /* search the table to see if the key exists */
  207.     ktp = &keytab[0];
  208.     found = FALSE;
  209.     while (ktp->k_fp != NULL) {
  210.         if (ktp->k_code == c) {
  211.             found = TRUE;
  212.             break;
  213.         }
  214.         ++ktp;
  215.     }
  216.  
  217.     /* if it isn't bound, bitch */
  218.     if (!found)
  219.         return(FALSE);
  220.  
  221.     /* save the pointer and scan to the end of the table */
  222.     sktp = ktp;
  223.     while (ktp->k_fp != NULL)
  224.         ++ktp;
  225.     --ktp;        /* backup to the last legit entry */
  226.  
  227.     /* copy the last entry to the current one */
  228.     sktp->k_code = ktp->k_code;
  229.     sktp->k_fp   = ktp->k_fp;
  230.  
  231.     /* null out the last one */
  232.     ktp->k_code = 0;
  233.     ktp->k_fp = NULL;
  234.     return(TRUE);
  235. }
  236.  
  237. PASCAL NEAR desbind(f, n) /* describe bindings
  238.            bring up a fake buffer and list the key bindings
  239.            into it with view mode            */
  240.  
  241. #if    APROP
  242. {
  243.     return(buildlist(TRUE, ""));
  244. }
  245.  
  246. PASCAL NEAR apro(f, n)    /* Apropos (List functions that match a substring) */
  247.  
  248. {
  249.     char mstring[NSTRING];    /* string to match cmd names to */
  250.     int status;        /* status return */
  251.  
  252.     status = mlreply("Apropos string: ", mstring, NSTRING - 1);
  253.     if (status != TRUE)
  254.         return(status);
  255.  
  256.     return(buildlist(FALSE, mstring));
  257. }
  258.  
  259. PASCAL NEAR buildlist(type, mstring)  /* build a binding list (limited or full) */
  260.  
  261. int type;    /* true = full list,   false = partial list */
  262. char *mstring;    /* match string if a partial list */
  263.  
  264. #endif
  265. {
  266.     register WINDOW *wp;    /* scanning pointer to windows */
  267.     register KEYTAB *ktp;    /* pointer into the command table */
  268.     register NBIND *nptr;    /* pointer into the name binding table */
  269.     register BUFFER *bp;    /* buffer to put binding list into */
  270.     char *strp;        /* pointer int string to send */
  271.     int cpos;        /* current position to use in outseq */
  272.     char outseq[80];    /* output buffer for keystroke sequence */
  273.  
  274.     /* split the current window to make room for the binding list */
  275.     if (splitwind(FALSE, 1) == FALSE)
  276.             return(FALSE);
  277.  
  278.     /* and get a buffer for it */
  279.     bp = bfind("Binding list", TRUE, 0);
  280.     if (bp == NULL || bclear(bp) == FALSE) {
  281.         mlwrite("Can not display binding list");
  282.         return(FALSE);
  283.     }
  284.  
  285.     /* let us know this is in progress */
  286.     mlwrite("[Building binding list]");
  287.  
  288.     /* disconect the current buffer */
  289.         if (--curbp->b_nwnd == 0) {             /* Last use.            */
  290.                 curbp->b_dotp  = curwp->w_dotp;
  291.                 curbp->b_doto  = curwp->w_doto;
  292.                 curbp->b_markp = curwp->w_markp;
  293.                 curbp->b_marko = curwp->w_marko;
  294.         curbp->b_fcol  = curwp->w_fcol;
  295.         }
  296.  
  297.     /* connect the current window to this buffer */
  298.     curbp = bp;    /* make this buffer current in current window */
  299.     bp->b_mode = 0;        /* no modes active in binding list */
  300.     bp->b_nwnd++;        /* mark us as more in use */
  301.     wp = curwp;
  302.     wp->w_bufp = bp;
  303.     wp->w_linep = bp->b_linep;
  304.     wp->w_flag = WFHARD|WFFORCE;
  305.     wp->w_dotp = bp->b_dotp;
  306.     wp->w_doto = bp->b_doto;
  307.     wp->w_markp = NULL;
  308.     wp->w_marko = 0;
  309.  
  310.     /* build the contents of this window, inserting it line by line */
  311.     nptr = &names[0];
  312.     while (nptr->n_func != NULL) {
  313.  
  314.         /* add in the command name */
  315.         strcpy(outseq, nptr->n_name);
  316.         cpos = strlen(outseq);
  317.         
  318. #if    APROP
  319.         /* if we are executing an apropos command..... */
  320.         if (type == FALSE &&
  321.             /* and current string doesn't include the search string */
  322.             strinc(outseq, mstring) == FALSE)
  323.             goto fail;
  324. #endif
  325.         /* search down any keys bound to this */
  326.         ktp = &keytab[0];
  327.         while (ktp->k_fp != NULL) {
  328.             if (ktp->k_fp == nptr->n_func) {
  329.                 /* padd out some spaces */
  330.                 while (cpos < 25)
  331.                     outseq[cpos++] = ' ';
  332.  
  333.                 /* add in the command sequence */
  334.                 cmdstr(ktp->k_code, &outseq[cpos]);
  335.                 strcat(outseq, "\r");
  336.  
  337.                 /* and add it as a line into the buffer */
  338.                 if (linstr(outseq) != TRUE)
  339.                     return(FALSE);
  340.  
  341.                 cpos = 0;    /* and clear the line */
  342.             }
  343.             ++ktp;
  344.         }
  345.  
  346.         /* if no key was bound, we need to dump it anyway */
  347.         if (cpos > 0) {
  348.             outseq[cpos++] = '\r';
  349.             outseq[cpos] = 0;
  350.             if (linstr(outseq) != TRUE)
  351.                 return(FALSE);
  352.         }
  353.  
  354. fail:        /* and on to the next name */
  355.         ++nptr;
  356.     }
  357.  
  358.     curwp->w_bufp->b_mode |= MDVIEW;/* put this buffer view mode */
  359.     curbp->b_flag &= ~BFCHG;    /* don't flag this as a change */
  360.     wp->w_dotp = lforw(bp->b_linep);/* back to the beginning */
  361.     wp->w_doto = 0;
  362.     wp = wheadp;            /* and update ALL mode lines */
  363.     while (wp != NULL) {
  364.         wp->w_flag |= WFMODE;
  365.         wp = wp->w_wndp;
  366.     }
  367.     mlwrite("");    /* clear the mode line */
  368.     return(TRUE);
  369. }
  370.  
  371. #if    APROP
  372. PASCAL NEAR strinc(source, sub)    /* does source include sub? */
  373.  
  374. char *source;    /* string to search in */
  375. char *sub;    /* substring to look for */
  376.  
  377. {
  378.     char *sp;    /* ptr into source */
  379.     char *nxtsp;    /* next ptr into source */
  380.     char *tp;    /* ptr into substring */
  381.  
  382.     /* for each character in the source string */
  383.     sp = source;
  384.     while (*sp) {
  385.         tp = sub;
  386.         nxtsp = sp;
  387.  
  388.         /* is the substring here? */
  389.         while (*tp) {
  390.             if (*nxtsp++ != *tp)
  391.                 break;
  392.             else
  393.                 tp++;
  394.         }
  395.  
  396.         /* yes, return a success */
  397.         if (*tp == 0)
  398.             return(TRUE);
  399.  
  400.         /* no, onward */
  401.         sp++;
  402.     }
  403.     return(FALSE);
  404. }
  405. #endif
  406.  
  407. /* get a command key sequence from the keyboard    */
  408.  
  409. unsigned int PASCAL NEAR getckey(mflag)
  410.  
  411. int mflag;    /* going for a meta sequence? */
  412.  
  413. {
  414.     register unsigned int c;    /* character fetched */
  415.     char tok[NSTRING];        /* command incoming */
  416.  
  417.     /* check to see if we are executing a command line */
  418.     if (clexec) {
  419.         macarg(tok);    /* get the next token */
  420.         return(stock(tok));
  421.     }
  422.  
  423.     /* or the normal way */
  424.     if (mflag)
  425.         c = get1key();
  426.     else
  427.         c = getcmd();
  428.     return(c);
  429. }
  430.  
  431. /* execute the startup file */
  432.  
  433. PASCAL NEAR startup(sfname)
  434.  
  435. char *sfname;    /* name of startup file (null if default) */
  436.  
  437. {
  438.     char *fname;    /* resulting file name to execute */
  439.  
  440.     /* look up the startup file */
  441.     if (*sfname != 0)
  442.         fname = flook(sfname, TRUE);
  443.     else
  444.         fname = flook(pathname[0], TRUE);
  445.  
  446.     /* if it isn't around, don't sweat it */
  447.     if (fname == NULL)
  448.         return(TRUE);
  449.  
  450.     /* otherwise, execute the sucker */
  451.     return(dofile(fname));
  452. }
  453.  
  454. /*    Look up the existance of a file along the normal or PATH
  455.     environment variable. Look first in the HOME directory if
  456.     asked and possible
  457. */
  458.  
  459. char *PASCAL NEAR flook(fname, hflag)
  460.  
  461. char *fname;    /* base file name to search for */
  462. int hflag;    /* Look in the HOME environment variable first? */
  463.  
  464. {
  465.     register char *home;    /* path to home directory */
  466.     register char *path;    /* environmental PATH variable */
  467.     register char *sp;    /* pointer into path spec */
  468.     register int i;        /* index */
  469.     static char fspec[NSTRING];    /* full path spec to search */
  470.     char *getenv();
  471.  
  472. #if    ENVFUNC
  473.  
  474.     if (hflag) {
  475. #if WMCS
  476.         home = getenv("SYS$HOME");
  477. #else
  478.         home = getenv("HOME");
  479. #endif
  480.         if (home != NULL) {
  481.             /* build home dir file spec */
  482.             strcpy(fspec, home);
  483. #if WMCS
  484.             strcat(fspec,fname);
  485. #else
  486.             strcat(fspec, "/");
  487.             strcat(fspec, fname);
  488. #endif
  489.  
  490.             /* and try it out */
  491.             if (ffropen(fspec) == FIOSUC) {
  492.                 ffclose();
  493.                 return(fspec);
  494.             }
  495.         }
  496.     }
  497. #endif
  498.  
  499.     /* always try the current directory first */
  500.     if (ffropen(fname) == FIOSUC) {
  501.         ffclose();
  502.         return(fname);
  503.     }
  504.  
  505. #if    ENVFUNC
  506.     /* get the PATH variable */
  507. #if WMCS
  508.     path = getenv("OPT$PATH");
  509. #else
  510.     path = getenv("PATH");
  511. #endif
  512.     if (path != NULL)
  513.         while (*path) {
  514.  
  515.             /* build next possible file spec */
  516.             sp = fspec;
  517. #if    ST520 & MWC
  518.             while (*path && (*path != PATHCHR) && (*path != ','))
  519. #else
  520.             while (*path && (*path != PATHCHR))
  521. #endif
  522.                 *sp++ = *path++;
  523.  
  524.             /* add a terminating dir separator if we need it */
  525.             if (sp != fspec)
  526. #if    ST520
  527.                 *sp++ = '\\';
  528. #else
  529.                 *sp++ = '/';
  530. #endif
  531.             *sp = 0;
  532.             strcat(fspec, fname);
  533.  
  534.             /* and try it out */
  535.             if (ffropen(fspec) == FIOSUC) {
  536.                 ffclose();
  537.                 return(fspec);
  538.             }
  539.  
  540. #if    ST520 & MWC
  541.             if ((*path == PATHCHR) || (*path == ','))
  542. #else
  543.             if (*path == PATHCHR)
  544. #endif
  545.                 ++path;
  546.         }
  547. #endif
  548.  
  549.     /* look it up via the old table method */
  550.     for (i=2; i < NPNAMES; i++) {
  551.         strcpy(fspec, pathname[i]);
  552.         strcat(fspec, fname);
  553.  
  554.         /* and try it out */
  555.         if (ffropen(fspec) == FIOSUC) {
  556.             ffclose();
  557.             return(fspec);
  558.         }
  559.     }
  560.  
  561.     return(NULL);    /* no such luck */
  562. }
  563.  
  564. PASCAL NEAR cmdstr(c, seq) /* change a key command to a string we can print out */
  565.  
  566. int c;        /* sequence to translate */
  567. char *seq;    /* destination string for sequence */
  568.  
  569. {
  570.     char *ptr;    /* pointer into current position in sequence */
  571.  
  572.     ptr = seq;
  573.  
  574.     /* apply meta sequence if needed */
  575.     if (c & META) {
  576.         *ptr++ = 'M';
  577.         *ptr++ = '-';
  578.     }
  579.  
  580.     /* apply ^X sequence if needed */
  581.     if (c & CTLX) {
  582.         *ptr++ = '^';
  583.         *ptr++ = 'X';
  584.     }
  585.  
  586.     /* apply SPEC sequence if needed */
  587.     if (c & SPEC) {
  588.         *ptr++ = 'F';
  589.         *ptr++ = 'N';
  590.     }
  591.  
  592.     /* apply ALTD sequence if needed */
  593.     if (c & ALTD) {
  594.         *ptr++ = 'M';
  595.         *ptr++ = 'S';
  596.     }
  597.  
  598.     /* apply control sequence if needed */
  599.     if (c & CTRL) {
  600.         *ptr++ = '^';
  601.     }
  602.  
  603.     c = c & 255;    /* strip the prefixes */
  604.  
  605.     /* and output the final sequence */
  606.  
  607.     *ptr++ = c;
  608.     *ptr = 0;    /* terminate the string */
  609. }
  610.  
  611. /*    This function looks a key binding up in the binding table    */
  612.  
  613. int (PASCAL NEAR *PASCAL NEAR getbind(c))()
  614.  
  615. int c;    /* key to find what is bound to it */
  616.  
  617. {
  618.     register KEYTAB *ktp;
  619.  
  620.         ktp = &keytab[0];                       /* Look in key table.   */
  621.         while (ktp->k_fp != NULL) {
  622.                 if (ktp->k_code == c)
  623.                         return(ktp->k_fp);
  624.                 ++ktp;
  625.         }
  626.  
  627.     /* no such binding */
  628.     return(NULL);
  629. }
  630.  
  631. /* getfname:    This function takes a ptr to function and gets the name
  632.         associated with it
  633. */
  634.  
  635. char *PASCAL NEAR getfname(func)
  636.  
  637. int (PASCAL NEAR *func)();    /* ptr to the requested function to bind to */
  638.  
  639. {
  640.     register NBIND *nptr;    /* pointer into the name binding table */
  641.  
  642.     /* skim through the table, looking for a match */
  643.     nptr = &names[0];
  644.     while (nptr->n_func != NULL) {
  645.         if (nptr->n_func == func)
  646.             return(nptr->n_name);
  647.         ++nptr;
  648.     }
  649.     return(NULL);
  650. }
  651.  
  652. /* fncmatch:    match fname to a function in the names table and return
  653.         any match or NULL if none */
  654.  
  655. int (PASCAL NEAR *PASCAL NEAR fncmatch(fname))()
  656.  
  657. char *fname;    /* name to attempt to match */
  658.  
  659. {
  660. #if    BINARY
  661.     register int nval;    /* value of matched name */
  662.  
  663.     nval = binary(fname, namval, numfunc);
  664.     if (nval == -1)
  665.         return(NULL);
  666.     else
  667.         return(names[nval].n_func);
  668. #else
  669.     register NBIND *ffp;    /* pointer to entry in name binding table */
  670.  
  671.     /* scan through the table, returning any match */
  672.     ffp = &names[0];
  673.     while (ffp->n_func != NULL) {
  674.         if (strcmp(fname, ffp->n_name) == 0)
  675.             return(ffp->n_func);
  676.         ++ffp;
  677.     }
  678.     return(NULL);
  679. #endif
  680. }
  681.  
  682. #if    BINARY
  683. char *PASCAL NEAR namval(index)
  684.  
  685. int index;    /* index of name to fetch out of the name table */
  686.  
  687. {
  688.     return(names[index].n_name);
  689. }
  690. #endif
  691.  
  692. /*    stock()        String key name TO Command Key
  693.  
  694.     A key binding consists of one or more prefix functions followed by
  695.     a keystroke.  The prefix functions are "M-" and "^X".  If both
  696.     occur, the M- must come first.  A keystroke can begin with FN or
  697.     MS to indicate function keys or mouse keys.  The "^" prefix may be
  698.     used to denote a control character with or without an FN or MS.
  699.  
  700.     Meta and ^X prefix of lower case letters are converted to upper
  701.     case.
  702. */
  703.  
  704. unsigned int PASCAL NEAR stock(keyname)
  705.  
  706. char *keyname;        /* name of key to translate to Command key form */
  707.  
  708. {
  709.     register unsigned int c;    /* key sequence to return */
  710.  
  711.     /* parse it up */
  712.     c = 0;
  713.  
  714.     /* then the META prefix */
  715.     if (*keyname == 'M' && *(keyname+1) == '-') {
  716.         c |= META;
  717.         keyname += 2;
  718.     }
  719.  
  720.     /* Do ^X prefix */
  721.     if(*keyname == '^' && *(keyname+1) == 'X') {
  722.         if(*(keyname+2) != 0) { /* Key is not bare ^X */
  723.             c |= CTLX;
  724.             keyname += 2;
  725.         }
  726.     }
  727.  
  728.     /* next the function prefix */
  729.     if (*keyname == 'F' && *(keyname+1) == 'N') {
  730.         c |= SPEC;
  731.         keyname += 2;
  732.     }
  733.  
  734.     /* and the mouse (ALTD) prefix */
  735.     if (*keyname == 'M' && *(keyname+1) == 'S') {
  736.         c |= ALTD;
  737.         keyname += 2;
  738.     }
  739.  
  740.     /* a control char?  (Always upper case) */
  741.     if (*keyname == '^' && *(keyname+1) != 0) {
  742.         c |= CTRL;
  743.         ++keyname;
  744.         if( islower( *keyname)) *keyname ^= DIFCASE;
  745.     }
  746.  
  747.     /* A literal control character? (Boo, hiss) */
  748.     if (*keyname < 32) {
  749.         c |= CTRL;
  750.         *keyname += '@';
  751.     }
  752.  
  753.     /* make sure we are not lower case if used with prefix */
  754.     if(!(c & (ALTD|SPEC)))    /* If not a special key */
  755.         if( c & (CTLX|META))/* If is a prefix */
  756.         if( islower( *keyname))/* Them make sure it's upper case */
  757.              *keyname ^= DIFCASE;
  758.  
  759.     /* the final sequence... */
  760.     c |= *keyname;
  761.     return(c);
  762. }
  763.  
  764. char *PASCAL NEAR transbind(skey)    /* string key name to binding name.... */
  765.  
  766. char *skey;    /* name of key to get binding for */
  767.  
  768. {
  769.     char *bindname;
  770.  
  771.     bindname = getfname(getbind(stock(skey)));
  772.     if (bindname == NULL)
  773.         bindname = "ERROR";
  774.  
  775.     return(bindname);
  776. }
  777.