home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / UE311C.ZIP / BIND.C < prev    next >
C/C++ Source or Header  |  1991-10-30  |  23KB  |  1,037 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    "eproto.h"
  10. #include    "edef.h"
  11. #include    "elang.h"
  12. #include    "epath.h"
  13.  
  14. PASCAL NEAR 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. int f,n;    /* prefix flag and argument */
  19.  
  20. {
  21.     register BUFFER *bp;    /* buffer pointer to help */
  22.     char *fname;        /* file name of help file */
  23.  
  24.     /* first check if we are already here */
  25.     bp = bfind("emacs.hlp", FALSE, BFINVS);
  26.  
  27.     if (bp == NULL) {
  28. #if SHARED
  29.         strcpy(tname, pathname[1]);
  30.         fname = flook(tname, FALSE);
  31. #else        
  32.         fname = flook(pathname[1], FALSE);
  33. #endif
  34.         if (fname == NULL) {
  35.             mlwrite(TEXT12);
  36. /*                "[Help file is not online]" */
  37.             return(FALSE);
  38.         }
  39.     }
  40.  
  41.     /* split the current window to make room for the help stuff */
  42.     if (splitwind(FALSE, 1) == FALSE)
  43.             return(FALSE);
  44.  
  45.     if (bp == NULL) {
  46.         /* and read the stuff in */
  47.         if (getfile(fname, FALSE) == FALSE)
  48.             return(FALSE);
  49.     } else
  50.         swbuffer(bp);
  51.  
  52.     /* make this window in VIEW mode, update all mode lines */
  53.     curwp->w_bufp->b_mode |= MDVIEW;
  54.     curwp->w_bufp->b_flag |= BFINVS;
  55.     upmode();
  56.     return(TRUE);
  57. }
  58.  
  59. PASCAL NEAR deskey(f, n)    /* describe the command for a certain key */
  60.  
  61. int f,n;    /* prefix flag and argument */
  62.  
  63. {
  64.     register int c;     /* key to describe */
  65.     register char *ptr;    /* string pointer to scan output strings */
  66.     char outseq[NSTRING];    /* output buffer for command sequence */
  67.  
  68.     /* prompt the user to type us a key to describe */
  69.     mlwrite(TEXT13);
  70. /*        ": describe-key " */
  71.  
  72.     /* get the command sequence to describe
  73.        change it to something we can print as well */
  74.     cmdstr(c = getckey(FALSE), &outseq[0]);
  75.  
  76.     /* and dump it out */
  77.     ostring(outseq);
  78.     ostring(" ");
  79.  
  80.     /* find the right ->function */
  81.     if ((ptr = getfname(getbind(c))) == NULL)
  82.         ptr = "Not Bound";
  83.  
  84.     /* output the command sequence */
  85.     ostring(ptr);
  86.     return(TRUE);
  87. }
  88.  
  89. /* bindtokey:    add a new key to the key binding table        */
  90.  
  91. PASCAL NEAR bindtokey(f, n)
  92.  
  93. int f, n;    /* command arguments [IGNORED] */
  94.  
  95. {
  96.     register unsigned int c;/* command key to bind */
  97.     register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
  98.     register KEYTAB *ktp;    /* pointer into the command table */
  99.     register int found;    /* matched command flag */
  100.     char outseq[80];    /* output buffer for keystroke sequence */
  101.  
  102.     /* prompt the user to type in a key to bind */
  103.     /* get the function name to bind it to */
  104.     kfunc = getname(TEXT15);
  105. /*            ": bind-to-key " */
  106.     if (kfunc == NULL) {
  107.         mlwrite(TEXT16);
  108. /*            "[No such function]" */
  109.         return(FALSE);
  110.     }
  111.     if (clexec == FALSE) {
  112.         ostring(" ");
  113.         TTflush();
  114.     }
  115.  
  116.     /* get the command sequence to bind */
  117.     c = getckey((kfunc == meta) || (kfunc == cex) ||
  118.             (kfunc == unarg) || (kfunc == ctrlg));
  119.  
  120.     if (clexec == FALSE) {
  121.  
  122.         /* change it to something we can print as well */
  123.         cmdstr(c, &outseq[0]);
  124.  
  125.         /* and dump it out */
  126.         ostring(outseq);
  127.     }
  128.  
  129.     /* if the function is a unique prefix key */
  130.     if (kfunc == unarg || kfunc == ctrlg || kfunc == quote) {
  131.  
  132.         /* search for an existing binding for the prefix key */
  133.         ktp = &keytab[0];
  134.         while (ktp->k_type != BINDNUL) {
  135.             if (ktp->k_ptr.fp == kfunc)
  136.                 unbindchar(ktp->k_code);
  137.             ++ktp;
  138.         }
  139.  
  140.         /* reset the appropriate global prefix variable */
  141.         if (kfunc == unarg)
  142.             reptc = c;
  143.         if (kfunc == ctrlg)
  144.             abortc = c;
  145.         if (kfunc == quote)
  146.             quotec = c;
  147.     }
  148.  
  149.     /* search the table to see if it exists */
  150.     ktp = &keytab[0];
  151.     found = FALSE;
  152.     while (ktp->k_type != BINDNUL) {
  153.         if (ktp->k_code == c) {
  154.             found = TRUE;
  155.             break;
  156.         }
  157.         ++ktp;
  158.     }
  159.  
  160.     if (found) {    /* it exists, just change it then */
  161.         ktp->k_ptr.fp = kfunc;
  162.         ktp->k_type = BINDFNC;
  163.     } else {    /* otherwise we need to add it to the end */
  164.         /* if we run out of binding room, bitch */
  165.         if (ktp >= &keytab[NBINDS]) {
  166.             mlwrite(TEXT17);
  167. /*                "Binding table FULL!" */
  168.             return(FALSE);
  169.         }
  170.  
  171.         ktp->k_code = c;    /* add keycode */
  172.         ktp->k_ptr.fp = kfunc;    /* and the function pointer */
  173.         ktp->k_type = BINDFNC;    /* and the binding type */
  174.         ++ktp;            /* and make sure the next is null */
  175.         ktp->k_code = 0;
  176.         ktp->k_type = BINDNUL;
  177.         ktp->k_ptr.fp = NULL;
  178.     }
  179.  
  180.     /* if we have rebound the meta key, make the
  181.        search terminator follow it            */
  182.     if (kfunc == meta)
  183.         sterm = c;
  184.  
  185.     return(TRUE);
  186. }
  187.  
  188. /* macrotokey:    Bind a key to a macro in the key binding table */
  189.  
  190. PASCAL NEAR macrotokey(f, n)
  191.  
  192. int f, n;    /* command arguments [IGNORED] */
  193.  
  194. {
  195.     register unsigned int c;/* command key to bind */
  196.     register BUFFER *kmacro;/* ptr to buffer of macro to bind to key */
  197.     register KEYTAB *ktp;    /* pointer into the command table */
  198.     register int found;    /* matched command flag */
  199.     register int status;    /* error return */
  200.     char outseq[80];    /* output buffer for keystroke sequence */
  201.     char bufn[NBUFN];    /* buffer to hold macro name */
  202.  
  203.     /* get the buffer name to use */
  204.     if ((status=mlreply(TEXT215, &bufn[1], NBUFN-2)) != TRUE)
  205. /*        ": macro-to-key " */
  206.         return(status);
  207.  
  208.     /* build the responce string for later */
  209.     strcpy(outseq, TEXT215);
  210. /*           ": macro-to-key " */
  211.     strcat(outseq, &bufn[1]);
  212.  
  213.     /* translate it to a buffer pointer */
  214.     bufn[0] = '[';
  215.     strcat(bufn, "]");
  216.     if ((kmacro=bfind(bufn, FALSE, 0)) == NULL) {
  217.         mlwrite(TEXT130);
  218. /*        "Macro not defined"*/
  219.         return(FALSE);
  220.     }
  221.  
  222.     strcat(outseq, " ");
  223.     mlwrite(outseq);
  224.  
  225.     /* get the command sequence to bind */
  226.     c = getckey(FALSE);
  227.  
  228.     /* change it to something we can print as well */
  229.     cmdstr(c, &outseq[0]);
  230.  
  231.     /* and dump it out */
  232.     ostring(outseq);
  233.  
  234.     /* search the table to see if it exists */
  235.     ktp = &keytab[0];
  236.     found = FALSE;
  237.     while (ktp->k_type != BINDNUL) {
  238.         if (ktp->k_code == c) {
  239.             found = TRUE;
  240.             break;
  241.         }
  242.         ++ktp;
  243.     }
  244.  
  245.     if (found) {    /* it exists, just change it then */
  246.         ktp->k_ptr.buf = kmacro;
  247.         ktp->k_type = BINDBUF;
  248.     } else {    /* otherwise we need to add it to the end */
  249.         /* if we run out of binding room, bitch */
  250.         if (ktp >= &keytab[NBINDS]) {
  251.             mlwrite(TEXT17);
  252. /*                "Binding table FULL!" */
  253.             return(FALSE);
  254.         }
  255.  
  256.         ktp->k_code = c;    /* add keycode */
  257.         ktp->k_ptr.buf = kmacro;    /* and the function pointer */
  258.         ktp->k_type = BINDBUF;    /* and the binding type */
  259.         ++ktp;            /* and make sure the next is null */
  260.         ktp->k_code = 0;
  261.         ktp->k_type = BINDNUL;
  262.         ktp->k_ptr.fp = NULL;
  263.     }
  264.  
  265.     return(TRUE);
  266. }
  267.  
  268. /* unbindkey:    delete a key from the key binding table */
  269.  
  270. PASCAL NEAR unbindkey(f, n)
  271.  
  272. int f, n;    /* command arguments [IGNORED] */
  273.  
  274. {
  275.     register int c;     /* command key to unbind */
  276.     char outseq[80];    /* output buffer for keystroke sequence */
  277.  
  278.     /* prompt the user to type in a key to unbind */
  279.     mlwrite(TEXT18);
  280. /*        ": unbind-key " */
  281.  
  282.     /* get the command sequence to unbind */
  283.     c = getckey(FALSE);        /* get a command sequence */
  284.  
  285.     /* change it to something we can print as well */
  286.     cmdstr(c, &outseq[0]);
  287.  
  288.     /* and dump it out */
  289.     ostring(outseq);
  290.  
  291.     /* if it isn't bound, bitch */
  292.     if (unbindchar(c) == FALSE) {
  293.         mlwrite(TEXT19);
  294. /*            "[Key not bound]" */
  295.         return(FALSE);
  296.     }
  297.     return(TRUE);
  298. }
  299.  
  300. PASCAL NEAR unbindchar(c)
  301.  
  302. int c;        /* command key to unbind */
  303.  
  304. {
  305.     register KEYTAB *ktp;    /* pointer into the command table */
  306.     register KEYTAB *sktp;    /* saved pointer into the command table */
  307.     register int found;    /* matched command flag */
  308.  
  309.     /* search the table to see if the key exists */
  310.     ktp = &keytab[0];
  311.     found = FALSE;
  312.     while (ktp->k_type != BINDNUL) {
  313.         if (ktp->k_code == c) {
  314.             found = TRUE;
  315.             break;
  316.         }
  317.         ++ktp;
  318.     }
  319.  
  320.     /* if it isn't bound, bitch */
  321.     if (!found)
  322.         return(FALSE);
  323.  
  324.     /* save the pointer and scan to the end of the table */
  325.     sktp = ktp;
  326.     while (ktp->k_type != BINDNUL)
  327.         ++ktp;
  328.     --ktp;        /* backup to the last legit entry */
  329.  
  330.     /* copy the last entry to the current one */
  331.     sktp->k_code = ktp->k_code;
  332.     sktp->k_type = ktp->k_type;
  333.     sktp->k_ptr.fp     = ktp->k_ptr.fp;
  334.  
  335.     /* null out the last one */
  336.     ktp->k_code = 0;
  337.     ktp->k_type = BINDNUL;
  338.     ktp->k_ptr.fp = NULL;
  339.     return(TRUE);
  340. }
  341.  
  342. /* Describe bindings:
  343.  
  344.        bring up a fake buffer and list the key bindings
  345.        into it with view mode
  346. */
  347.  
  348. PASCAL NEAR desbind(f, n)
  349.  
  350. int f,n;    /* prefix flag and argument */
  351.  
  352. {
  353.     return(buildlist(TRUE, ""));
  354. }
  355.  
  356. PASCAL NEAR apro(f, n)    /* Apropos (List functions that match a substring) */
  357.  
  358. int f,n;    /* prefix flag and argument */
  359.  
  360. {
  361.     char mstring[NSTRING];    /* string to match cmd names to */
  362.     int status;        /* status return */
  363.  
  364.     status = mlreply(TEXT20, mstring, NSTRING - 1);
  365. /*             "Apropos string: " */
  366.     if (status != TRUE)
  367.         return(status);
  368.  
  369.     return(buildlist(FALSE, mstring));
  370. }
  371.  
  372. PASCAL NEAR buildlist(type, mstring)  /* build a binding list (limited or full) */
  373.  
  374. int type;    /* true = full list,   false = partial list */
  375. char *mstring;    /* match string if a partial list */
  376.  
  377. {
  378.     register KEYTAB *ktp;    /* pointer into the command table */
  379.     register NBIND *nptr;    /* pointer into the name binding table */
  380.     register BUFFER *listbuf;/* buffer to put binding list into */
  381.     register BUFFER *bp;    /* buffer ptr for function scan */
  382.     int cpos;        /* current position to use in outseq */
  383.     char outseq[80];    /* output buffer for keystroke sequence */
  384.  
  385.     /* get a buffer for the binding list */
  386.     listbuf = bfind(TEXT21, TRUE, 0);
  387. /*           "Binding list" */
  388.     if (listbuf == NULL || bclear(listbuf) == FALSE) {
  389.         mlwrite(TEXT22);
  390. /*            "Can not display binding list" */
  391.         return(FALSE);
  392.     }
  393.  
  394.     /* let us know this is in progress */
  395.     mlwrite(TEXT23);
  396. /*        "[Building binding list]" */
  397.  
  398.     /* build the contents of this window, inserting it line by line */
  399.     nptr = &names[0];
  400.     while (nptr->n_func != NULL) {
  401.  
  402.         /* add in the command name */
  403.         strcpy(outseq, nptr->n_name);
  404.         cpos = strlen(outseq);
  405.  
  406.         /* if we are executing an apropos command..... */
  407.         if (type == FALSE &&
  408.             /* and current string doesn't include the search string */
  409.             strinc(outseq, mstring) == FALSE)
  410.             goto fail;
  411.  
  412.         /* search down any keys bound to this */
  413.         ktp = &keytab[0];
  414.         while (ktp->k_type != BINDNUL) {
  415.             if (ktp->k_type == BINDFNC &&
  416.                 ktp->k_ptr.fp == nptr->n_func) {
  417.                 /* padd out some spaces */
  418.                 while (cpos < 25)
  419.                     outseq[cpos++] = ' ';
  420.  
  421.                 /* add in the command sequence */
  422.                 cmdstr(ktp->k_code, &outseq[cpos]);
  423.  
  424.                 /* and add it as a line into the buffer */
  425.                 if (addline(listbuf, outseq) != TRUE)
  426.                     return(FALSE);
  427.  
  428.                 cpos = 0;    /* and clear the line */
  429.             }
  430.             ++ktp;
  431.         }
  432.  
  433.         /* if no key was bound, we need to dump it anyway */
  434.         if (cpos > 0) {
  435.             outseq[cpos] = 0;
  436.             if (addline(listbuf, outseq) != TRUE)
  437.                 return(FALSE);
  438.         }
  439.  
  440. fail:        /* and on to the next name */
  441.         ++nptr;
  442.     }
  443.  
  444.     /* add a blank line between the key and macro lists */
  445.     if (addline(listbuf, "") != TRUE)
  446.         return(FALSE);
  447.  
  448.     /* scan all buffers looking for macroes and their bindings */
  449.     bp = bheadp;
  450.     while (bp) {
  451.  
  452.         /* is this buffer a macro? */
  453.         if (bp->b_bname[0] != '[')
  454.             goto bfail;
  455.  
  456.         /* add in the command name */
  457.         strcpy(outseq, bp->b_bname);
  458.         cpos = strlen(outseq);
  459.  
  460.         /* if we are executing an apropos command..... */
  461.         if (type == FALSE &&
  462.             /* and current string doesn't include the search string */
  463.             strinc(outseq, mstring) == FALSE)
  464.             goto bfail;
  465.  
  466.         /* search down any keys bound to this macro */
  467.         ktp = &keytab[0];
  468.         while (ktp->k_type != BINDNUL) {
  469.             if (ktp->k_type == BINDBUF &&
  470.                 ktp->k_ptr.buf == bp) {
  471.                 /* padd out some spaces */
  472.                 while (cpos < 25)
  473.                     outseq[cpos++] = ' ';
  474.  
  475.                 /* add in the command sequence */
  476.                 cmdstr(ktp->k_code, &outseq[cpos]);
  477.  
  478.                 /* and add it as a line into the buffer */
  479.                 if (addline(listbuf, outseq) != TRUE)
  480.                     return(FALSE);
  481.  
  482.                 cpos = 0;    /* and clear the line */
  483.             }
  484.             ++ktp;
  485.         }
  486.  
  487.         /* if no key was bound, we need to dump it anyway */
  488.         if (cpos > 0) {
  489.             outseq[cpos] = 0;
  490.             if (addline(listbuf, outseq) != TRUE)
  491.                 return(FALSE);
  492.         }
  493.  
  494. bfail:        /* and on to the next buffer */
  495.         bp = bp->b_bufp;
  496.     }
  497.  
  498.     wpopup(listbuf);
  499.     mlerase();    /* clear the mode line */
  500.     return(TRUE);
  501. }
  502.  
  503. PASCAL NEAR strinc(source, sub) /* does source include sub? */
  504.  
  505. char *source;    /* string to search in */
  506. char *sub;    /* substring to look for */
  507.  
  508. {
  509.     char *sp;    /* ptr into source */
  510.     char *nxtsp;    /* next ptr into source */
  511.     char *tp;    /* ptr into substring */
  512.  
  513.     /* for each character in the source string */
  514.     sp = source;
  515.     while (*sp) {
  516.         tp = sub;
  517.         nxtsp = sp;
  518.  
  519.         /* is the substring here? */
  520.         while (*tp) {
  521.             if (*nxtsp++ != *tp)
  522.                 break;
  523.             else
  524.                 tp++;
  525.         }
  526.  
  527.         /* yes, return a success */
  528.         if (*tp == 0)
  529.             return(TRUE);
  530.  
  531.         /* no, onward */
  532.         sp++;
  533.     }
  534.     return(FALSE);
  535. }
  536.  
  537. /* get a command key sequence from the keyboard */
  538.  
  539. unsigned int PASCAL NEAR getckey(mflag)
  540.  
  541. int mflag;    /* going for a meta sequence? */
  542.  
  543. {
  544.     register unsigned int c;    /* character fetched */
  545.     char tok[NSTRING];        /* command incoming */
  546.  
  547.     /* check to see if we are executing a command line */
  548.     if (clexec) {
  549.         macarg(tok);    /* get the next token */
  550.         return(stock(tok));
  551.     }
  552.  
  553.     /* or the normal way */
  554.     if (mflag)
  555.         c = getkey();
  556.     else
  557.         c = getcmd();
  558.     return(c);
  559. }
  560.  
  561. /* execute the startup file */
  562.  
  563. PASCAL NEAR startup(sfname)
  564.  
  565. char *sfname;    /* name of startup file (null if default) */
  566.  
  567. {
  568.     char *fname;    /* resulting file name to execute */
  569.     char name[NSTRING];    /* name with extention */
  570.  
  571.     /* look up the startup file */
  572.     if (*sfname != 0) {
  573.  
  574.          /* default the extention */
  575.         strcpy(name, sfname);
  576.         if (sindex(name, ".") == 0)
  577.             strcat(name, ".cmd");
  578.  
  579.         fname = flook(name, TRUE);
  580.     } else
  581. #if SHARED
  582.     {
  583.         strcpy(tname, pathname[0]);
  584.         fname = flook(tname, TRUE);
  585.     }
  586. #else
  587.         fname = flook(pathname[0], TRUE);
  588. #endif
  589.  
  590.     /* if it isn't around, don't sweat it */
  591.     if (fname == NULL)
  592.         return(TRUE);
  593.  
  594.     /* otherwise, execute the sucker */
  595.     return(dofile(fname));
  596. }
  597.  
  598. /*    Look up the existance of a file along the normal or PATH
  599.     environment variable.
  600.  
  601.     LOOKUP ORDER:
  602.  
  603.         if contains path:
  604.  
  605.             absolute
  606.  
  607.         else
  608.  
  609.             HOME environment directory
  610.             all directories along PATH environment
  611.             directories in table from EPATH.H
  612. */
  613.  
  614. char *PASCAL NEAR flook(fname, hflag)
  615.  
  616. char *fname;    /* base file name to search for */
  617. int hflag;    /* Look in the HOME environment variable first? */
  618.  
  619. {
  620.     register char *home;    /* path to home directory */
  621.     register char *path;    /* environmental PATH variable */
  622.     register char *sp;    /* pointer into path spec */
  623.     register int i;     /* index */
  624.     static char fspec[NFILEN];    /* full path spec to search */
  625.     char *getenv();
  626.  
  627.     /* if we have an absolute path.. check only there! */
  628.     sp = fname;
  629.     while (*sp) {
  630.         if (*sp == ':' || *sp == '\\' || *sp == '/') {
  631.             if (ffropen(fname) == FIOSUC) {
  632.                 ffclose();
  633.                 return(fname);
  634.             } else
  635.                 return(NULL);
  636.         }
  637.         ++sp;
  638.     }
  639.  
  640. #if    ENVFUNC
  641.  
  642.     if (hflag) {
  643. #if WMCS
  644.         home = getenv("SYS$HOME");
  645. #else
  646.         home = getenv("HOME");
  647. #endif
  648.         if (home != NULL) {
  649.             /* build home dir file spec */
  650.             strcpy(fspec, home);
  651. #if WMCS
  652.             strcat(fspec,fname);
  653. #else
  654.             strcat(fspec, DIRSEPSTR);
  655.             strcat(fspec, fname);
  656. #endif
  657.  
  658.             /* and try it out */
  659.             if (ffropen(fspec) == FIOSUC) {
  660.                 ffclose();
  661.                 return(fspec);
  662.             }
  663.         }
  664.     }
  665. #endif
  666.  
  667. #if    ENVFUNC
  668.     /* get the PATH variable */
  669. #if WMCS
  670.     path = getenv("OPT$PATH");
  671. #else
  672.     path = getenv("PATH");
  673. #endif
  674.     if (path != NULL)
  675.         while (*path) {
  676.  
  677.             /* build next possible file spec */
  678.             sp = fspec;
  679. #if    TOS
  680.             while (*path && (*path != PATHCHR) && (*path != ','))
  681. #else
  682.             while (*path && (*path != PATHCHR))
  683. #endif
  684.                 *sp++ = *path++;
  685.  
  686.             /* add a terminating dir separator if we need it */
  687.             if (*(sp-1) != DIRSEPCHAR)
  688.                 *sp++ = DIRSEPCHAR;
  689.             *sp = 0;
  690.             strcat(fspec, fname);
  691.  
  692.             /* and try it out */
  693.             if (ffropen(fspec) == FIOSUC) {
  694.                 ffclose();
  695.                 return(fspec);
  696.             }
  697.  
  698. #if    TOS && MWC
  699.             if ((*path == PATHCHR) || (*path == ','))
  700. #else
  701.             if (*path == PATHCHR)
  702. #endif
  703.                 ++path;
  704.         }
  705. #endif
  706.  
  707.     /* look it up via the old table method */
  708.     for (i=2; i < NPNAMES; i++) {
  709.         strcpy(fspec, pathname[i]);
  710.         strcat(fspec, fname);
  711.  
  712.         /* and try it out */
  713.         if (ffropen(fspec) == FIOSUC) {
  714.             ffclose();
  715.             return(fspec);
  716.         }
  717.     }
  718.  
  719.     return(NULL);    /* no such luck */
  720. }
  721.  
  722. PASCAL NEAR cmdstr(c, seq) /* change a key command to a string we can print out */
  723.  
  724. int c;        /* sequence to translate */
  725. char *seq;    /* destination string for sequence */
  726.  
  727. {
  728.     char *ptr;    /* pointer into current position in sequence */
  729.  
  730.     ptr = seq;
  731.  
  732.     /* apply ^X sequence if needed */
  733.     if (c & CTLX) {
  734.         *ptr++ = '^';
  735.         *ptr++ = 'X';
  736.     }
  737.  
  738.     /* apply ALT key sequence if needed */
  739.     if (c & ALTD) {
  740.         *ptr++ = 'A';
  741.         *ptr++ = '-';
  742.     }
  743.  
  744.     /* apply Shifted sequence if needed */
  745.     if (c & SHFT) {
  746.         *ptr++ = 'S';
  747.         *ptr++ = '-';
  748.     }
  749.  
  750.     /* apply MOUS sequence if needed */
  751.     if (c & MOUS) {
  752.         *ptr++ = 'M';
  753.         *ptr++ = 'S';
  754.     }
  755.  
  756.     /* apply meta sequence if needed */
  757.     if (c & META) {
  758.         *ptr++ = 'M';
  759.         *ptr++ = '-';
  760.     }
  761.  
  762.     /* apply SPEC sequence if needed */
  763.     if (c & SPEC) {
  764.         *ptr++ = 'F';
  765.         *ptr++ = 'N';
  766.     }
  767.  
  768.     /* apply control sequence if needed */
  769.     if (c & CTRL) {
  770.         *ptr++ = '^';
  771.     }
  772.  
  773.     c = c & 255;    /* strip the prefixes */
  774.  
  775.     /* and output the final sequence */
  776.  
  777.     *ptr++ = c;
  778.     *ptr = 0;    /* terminate the string */
  779. }
  780.  
  781. /*    This function looks a key binding up in the binding table    */
  782.  
  783. KEYTAB *getbind(c)
  784.  
  785. register int c;    /* key to find what is bound to it */
  786.  
  787. {
  788.     register KEYTAB *ktp;
  789.  
  790.     /* scan through the binding table, looking for the key's entry */
  791.     ktp = &keytab[0];
  792.     while (ktp->k_type != BINDNUL) {
  793.         if (ktp->k_code == c)
  794.             return(ktp);
  795.         ++ktp;
  796.     }
  797.  
  798.     /* no such binding */
  799.     return((KEYTAB *)NULL);
  800. }
  801.  
  802. /* getfname:    This function takes a ptr to KEYTAB entry and gets the name
  803.         associated with it
  804. */
  805.  
  806. char *PASCAL NEAR getfname(key)
  807.  
  808. KEYTAB *key;    /* key binding to return a name of */
  809.  
  810. {
  811.     int (PASCAL NEAR *func)(); /* ptr to the requested function */
  812.     register NBIND *nptr;    /* pointer into the name binding table */
  813.     register BUFFER *bp;    /* ptr to buffer to test */
  814.     register BUFFER *kbuf;    /* ptr to requested buffer */
  815.  
  816.     /* if this isn't a valid key, it has no name */
  817.     if (key == NULL)
  818.         return(NULL);
  819.  
  820.     /* skim through the binding table, looking for a match */
  821.     if (key->k_type == BINDFNC) {
  822.         func = key->k_ptr.fp;
  823.         nptr = &names[0];
  824.         while (nptr->n_func != NULL) {
  825.             if (nptr->n_func == func)
  826.                 return(nptr->n_name);
  827.             ++nptr;
  828.         }
  829.         return(NULL);
  830.     }
  831.  
  832.     /* skim through the buffer list looking for a match */
  833.     kbuf = key->k_ptr.buf;
  834.     bp = bheadp;
  835.     while (bp) {
  836.         if (bp == kbuf)
  837.             return(bp->b_bname);
  838.         bp = bp->b_bufp;
  839.     }
  840.     return(NULL);
  841. }
  842.  
  843. /* fncmatch:    match fname to a function in the names table and return
  844.         any match or NULL if none */
  845.  
  846. #if    MSC
  847. int (PASCAL NEAR *PASCAL NEAR fncmatch(char *fname))(void)
  848. #else
  849. int (PASCAL NEAR *PASCAL NEAR fncmatch(fname))()
  850.  
  851. char *fname;    /* name to attempt to match */
  852. #endif
  853.  
  854. {
  855.     int nval;
  856.  
  857.     if ((nval = binary(fname, namval, numfunc)) == -1)
  858.         return(NULL);
  859.     else
  860.         return(names[nval].n_func);
  861. }
  862.  
  863. char *PASCAL NEAR namval(index)
  864.  
  865. int index;    /* index of name to fetch out of the name table */
  866.  
  867. {
  868.     return(names[index].n_name);
  869. }
  870.  
  871. /*    stock()     String key name TO Command Key
  872.  
  873.     A key binding consists of one or more prefix functions followed by
  874.     a keystroke.  Allowable prefixes must be in the following order:
  875.  
  876.     ^X    preceeding control-X
  877.     A-    similtaneous ALT key (on PCs mainly)
  878.     S-    shifted function key
  879.     MS    mouse generated keystroke
  880.     M-    Preceding META key
  881.     FN    function key
  882.     ^    control key
  883.  
  884.     Meta and ^X prefix of lower case letters are converted to upper
  885.     case.  Real control characters are automatically converted to
  886.     the ^A form.
  887. */
  888.  
  889. unsigned int PASCAL NEAR stock(keyname)
  890.  
  891. char *keyname;    /* name of key to translate to Command key form */
  892.  
  893. {
  894.     register unsigned int c;    /* key sequence to return */
  895.  
  896.     /* parse it up */
  897.     c = 0;
  898.  
  899.     /* Do ^X prefix */
  900.     if(*keyname == '^' && *(keyname+1) == 'X') {
  901.         if(*(keyname+2) != 0) { /* Key is not bare ^X */
  902.             c |= CTLX;
  903.             keyname += 2;
  904.         }
  905.     }
  906.  
  907.     /* and the ALT key prefix */
  908.     if (*keyname == 'A' && *(keyname+1) == '-') {
  909.         c |= ALTD;
  910.         keyname += 2;
  911.     }
  912.  
  913.     /* and the SHIFTED prefix */
  914.     if (*keyname == 'S' && *(keyname+1) == '-') {
  915.         c |= SHFT;
  916.         keyname += 2;
  917.     }
  918.  
  919.     /* and the mouse (MOUS) prefix */
  920.     if (*keyname == 'M' && *(keyname+1) == 'S') {
  921.         c |= MOUS;
  922.         keyname += 2;
  923.     }
  924.  
  925.     /* then the META prefix */
  926.     if (*keyname == 'M' && *(keyname+1) == '-') {
  927.         c |= META;
  928.         keyname += 2;
  929.     }
  930.  
  931.     /* next the function prefix */
  932.     if (*keyname == 'F' && *(keyname+1) == 'N') {
  933.         c |= SPEC;
  934.         keyname += 2;
  935.     }
  936.  
  937.     /* a control char?  (Always upper case) */
  938.     if (*keyname == '^' && *(keyname+1) != 0) {
  939.         c |= CTRL;
  940.         ++keyname;
  941.         uppercase(keyname);
  942.     }
  943.  
  944.     /* A literal control character? (Boo, hiss) */
  945.     if (*keyname < 32) {
  946.         c |= CTRL;
  947.         *keyname += '@';
  948.     }
  949.  
  950.     /* make sure we are not lower case if used with ^X or M- */
  951.     if(!(c & (MOUS|SPEC|ALTD|SHFT)))    /* If not a special key */
  952.         if( c & (CTLX|META))        /* If is a prefix */
  953.         uppercase(keyname);        /* Then make sure it's upper case */
  954.  
  955.     /* the final sequence... */
  956.     c |= *keyname;
  957.     return(c);
  958. }
  959.  
  960. char *PASCAL NEAR transbind(skey)    /* string key name to binding name.... */
  961.  
  962. char *skey;    /* name of key to get binding for */
  963.  
  964. {
  965.     char *bindname;
  966.  
  967.     bindname = getfname(getbind(stock(skey)));
  968.     if (bindname == NULL)
  969.         bindname = errorm;
  970.  
  971.     return(bindname);
  972. }
  973.  
  974. int PASCAL NEAR execkey(key, f, n)    /* execute a function bound to a key */
  975.  
  976. KEYTAB *key;    /* key to execute */
  977. int f, n;    /* agruments to C function */
  978.  
  979. {
  980.     register int status;    /* error return */
  981.  
  982.     if (key->k_type == BINDFNC)
  983.         return((*(key->k_ptr.fp))(f, n));
  984.     if (key->k_type == BINDBUF) {
  985.         while (n--) {
  986.             status = dobuf(key->k_ptr.buf);
  987.             if (status != TRUE)
  988.                 return(status);
  989.         }
  990.     }
  991.     return(TRUE);
  992. }
  993.  
  994. /* set a KEYTAB to the given name of the given type */
  995.  
  996. #if    PROTO
  997. int setkey(KEYTAB *key, char *name)
  998. #else
  999. setkey(key, type, name)
  1000.  
  1001. KEYTAB *key;        /* ptr to key to set */
  1002. char *name;        /* name of function or buffer */
  1003. #endif
  1004. {
  1005.     int (PASCAL NEAR *ktemp)();    /* temp function pointer to assign */
  1006.     register BUFFER *kmacro;    /* ptr to buffer of macro to bind to key */
  1007.     char bufn[NBUFN];        /* buffer to hold macro name */
  1008.  
  1009.     /* are we unbinding it? */
  1010.     if (*name == 0) {
  1011.         key->k_type = BINDNUL;
  1012.         return(TRUE);
  1013.     }
  1014.  
  1015.     /* bind to a built in function? */
  1016.     if ((ktemp = fncmatch(name)) != NULL) {
  1017.         key->k_ptr.fp = ktemp;
  1018.         key->k_type = BINDFNC;
  1019.         return(TRUE);
  1020.     }
  1021.  
  1022.     /* is it a procedure/macro? */
  1023.     strcpy(bufn, "[");
  1024.     strcat(bufn, name);
  1025.     strcat(bufn, "]");
  1026.     if ((kmacro=bfind(bufn, FALSE, 0)) != NULL) {
  1027.         key->k_ptr.buf = kmacro;
  1028.         key->k_type = BINDBUF;
  1029.         return(TRUE);
  1030.     }
  1031.  
  1032.     /* not anything we can bind to */
  1033.     mlwrite(TEXT16);
  1034. /*        "[No such function]" */
  1035.     return(FALSE);
  1036. }
  1037.