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