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