home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / VILE327.ZIP / VILE327.TAR / vile3.27 / bind.c < prev    next >
C/C++ Source or Header  |  1992-12-14  |  24KB  |  1,065 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.  * $Log: bind.c,v $
  7.  * Revision 1.25  1992/12/04  09:08:45  foxharp
  8.  * deleted unused assigns
  9.  *
  10.  * Revision 1.24  1992/08/20  23:40:48  foxharp
  11.  * typo fixes -- thanks, eric
  12.  *
  13.  * Revision 1.23  1992/06/04  19:45:14  foxharp
  14.  * cast strlen() to int for new ANSI promotion semantics :-(
  15.  *
  16.  * Revision 1.22  1992/05/19  08:55:44  foxharp
  17.  * more prototype and shadowed decl fixups
  18.  *
  19.  * Revision 1.21  1992/05/16  12:00:31  pgf
  20.  * prototypes/ansi/void-int stuff/microsoftC
  21.  *
  22.  * Revision 1.20  1992/03/13  08:44:53  pgf
  23.  * honor \n like \r in kbd_engl_stat
  24.  *
  25.  * Revision 1.19  1992/03/05  09:19:55  pgf
  26.  * changed some mlwrite() to mlforce(), due to new terse support
  27.  *
  28.  * Revision 1.18  1992/01/05  00:06:13  pgf
  29.  * split mlwrite into mlwrite/mlprompt/mlforce to make errors visible more
  30.  * often.  also normalized message appearance somewhat.
  31.  *
  32.  * Revision 1.17  1992/01/03  23:31:49  pgf
  33.  * use new ch_fname() to manipulate filenames, since b_fname is now
  34.  * a malloc'ed sting, to avoid length limits
  35.  *
  36.  * Revision 1.16  1991/11/01  14:38:00  pgf
  37.  * saber cleanup
  38.  *
  39.  * Revision 1.15  1991/10/22  14:08:23  pgf
  40.  * took out old ifdef BEFORE code
  41.  *
  42.  * Revision 1.14  1991/09/27  02:49:01  pgf
  43.  * removed scalar init of static array
  44.  *
  45.  * Revision 1.13  1991/09/19  13:33:48  pgf
  46.  * MDEXACT changed to MDIGNCASE
  47.  *
  48.  * Revision 1.12  1991/08/12  15:05:14  pgf
  49.  * added fnc2key function, for getting back into insert mode
  50.  *
  51.  * Revision 1.11  1991/08/07  12:35:07  pgf
  52.  * added RCS log messages
  53.  *
  54.  * revision 1.10
  55.  * date: 1991/08/06 15:10:56;
  56.  * global/local values
  57.  * and new printf/list stuff
  58.  * 
  59.  * revision 1.9
  60.  * date: 1991/06/27 19:45:08;
  61.  * fixed prompts
  62.  * 
  63.  * revision 1.8
  64.  * date: 1991/06/03 17:34:51;
  65.  * switch from "meta" etc. to "ctla" etc.
  66.  * 
  67.  * revision 1.7
  68.  * date: 1991/06/03 13:58:22;
  69.  * made bind description list better
  70.  * 
  71.  * revision 1.6
  72.  * date: 1991/06/03 10:18:31;
  73.  * fix apropos bug, and a bind nit
  74.  * 
  75.  * revision 1.5
  76.  * date: 1991/05/31 10:31:34;
  77.  * new kbd_engl_stat() routine, which returns more status, for use in the
  78.  * new namedcmd() code
  79.  * 
  80.  * revision 1.4
  81.  * date: 1990/12/06 19:49:07;
  82.  * always rebuild Binding List buffer on request
  83.  * 
  84.  * revision 1.3
  85.  * date: 1990/10/03 16:00:30;
  86.  * make backspace work for everyone
  87.  * 
  88.  * revision 1.2
  89.  * date: 1990/09/28 14:34:57;
  90.  * changed prc2kcod decl to int
  91.  * 
  92.  * revision 1.1
  93.  * date: 1990/09/21 10:24:44;
  94.  * initial vile RCS revision
  95. */
  96.  
  97. #include    <stdio.h>
  98. #include    "estruct.h"
  99. #include    "edef.h"
  100. #include    "epath.h"
  101.  
  102. /* dummy prefix binding functions */
  103. extern CMDFUNC f_cntl_af, f_cntl_xf, f_unarg, f_esc;
  104.  
  105. /* give me some help!!!! bring up a buffer and read the help file into it */
  106. /* ARGSUSED */
  107. int
  108. help(f, n)
  109. int f,n;
  110. {
  111.     register BUFFER *bp;    /* buffer pointer to help */
  112.     char *fname;        /* ptr to file returned by flook() */
  113.  
  114.     /* first check if we are already here */
  115.     bp = bfind("[Help]", OK_CREAT, BFSCRTCH);
  116.     if (bp == NULL)
  117.         return FALSE;
  118.  
  119.     if (bp->b_active == FALSE) { /* never been used */
  120.         fname = flook(pathname[1], FL_ANYWHERE);
  121.         if (fname == NULL) {
  122.             mlforce("[Sorry, can't find the help information]");
  123.             zotbuf(bp);
  124.             return(FALSE);
  125.         }
  126.         /* and read the stuff in */
  127.         if (readin(fname, 0, bp, TRUE) == FALSE ||
  128.                 popupbuff(bp) == FALSE) {
  129.             zotbuf(bp);
  130.             return(FALSE);
  131.         }
  132.         strcpy(bp->b_bname,"[Help]");
  133.         {
  134.             char buf[80];
  135.                 lsprintf(buf, "       %s   %s",prognam,version);
  136.             ch_fname(bp, buf);
  137.         }
  138.         make_local_b_val(bp,MDVIEW);    /* make it readonly, */
  139.         set_b_val(bp,MDVIEW,TRUE);
  140.         make_local_b_val(bp,MDIGNCASE); /* easy to search, */
  141.         set_b_val(bp,MDIGNCASE,TRUE);
  142.         make_local_b_val(bp,VAL_TAB);    /* and tabbed by 8 */
  143.         set_b_val(bp,VAL_TAB,8);
  144.         bp->b_flag |= BFSCRTCH;
  145.     }
  146.     return swbuffer(bp);
  147. }
  148.  
  149. #if REBIND
  150.  
  151. /* ARGSUSED */
  152. int
  153. deskey(f, n)    /* describe the command for a certain key */
  154. int f,n;
  155. {
  156.     register int c;        /* key to describe */
  157.     register char *ptr;    /* string pointer to scan output strings */
  158.     char outseq[NSTRING];    /* output buffer for command sequence */
  159.     char *kcod2prc();
  160.  
  161.     /* prompt the user to type us a key to describe */
  162.     mlprompt("Describe the function bound to this key sequence: ");
  163.  
  164.     /* get the command sequence to describe
  165.        change it to something we can print as well */
  166.  
  167.     /* check to see if we are executing a command line */
  168.     if (clexec) {
  169.         char tok[NSTRING];
  170.         macarg(tok);    /* get the next token */
  171.         c = prc2kcod(tok);
  172.     } else {
  173.         c = kbd_seq();
  174.     }
  175.     kcod2prc(c, &outseq[0]);
  176.  
  177.     /* and dump it out */
  178.     ostring(outseq);
  179.     ostring(" ");
  180.  
  181.     /* find the right ->function */
  182.     if ((ptr = fnc2engl(kcod2fnc(c))) == NULL)
  183.         ptr = "Not Bound";
  184.  
  185.     /* output the command sequence */
  186.     ostring(ptr);
  187.     return TRUE;
  188. }
  189.  
  190. /* bindkey:    add a new key to the key binding table        */
  191.  
  192. /* ARGSUSED */
  193. int
  194. bindkey(f, n)
  195. int f, n;    /* command arguments [IGNORED] */
  196. {
  197.     register int c;        /* command key to bind */
  198.     register CMDFUNC *kcmd;    /* ptr to the requested function to bind to */
  199.     register KBIND *kbp;    /* pointer into a binding table */
  200.     char outseq[80];    /* output buffer for keystroke sequence */
  201.     char *fnp;
  202.     char *kbd_engl();
  203.     char *kcod2prc();
  204.  
  205.     /* prompt the user to type in a key to bind */
  206.     mlprompt("Bind function with english name: ");
  207.  
  208.     /* get the function name to bind it to */
  209. #if    NeWS
  210.     newsimmediateon() ;
  211. #endif
  212.     fnp = kbd_engl();
  213. #if    NeWS
  214.     newsimmediateoff() ;
  215. #endif
  216.  
  217.     if (fnp == NULL || (kcmd = engl2fnc(fnp)) == NULL) {
  218.         mlforce("[No such function]");
  219.         return(FALSE);
  220.     }
  221.     mlprompt("...to keyboard sequence (type it exactly): ");
  222.  
  223.     /* get the command sequence to bind */
  224.     if (clexec) {
  225.         char tok[NSTRING];
  226.         macarg(tok);    /* get the next token */
  227.         c = prc2kcod(tok);
  228.     } else {
  229.         /* perhaps we only want a single key, not a sequence */
  230.         /*     (see more comments below) */
  231.         if ((kcmd == &f_cntl_af) || (kcmd == &f_cntl_xf) ||
  232.                 (kcmd == &f_unarg) || (kcmd == &f_esc))
  233.             c = kbd_key();
  234.         else
  235.             c = kbd_seq();
  236.     }
  237.  
  238.     /* change it to something we can print as well */
  239.     kcod2prc(c, &outseq[0]);
  240.  
  241.     /* and dump it out */
  242.     ostring(outseq);
  243.  
  244.     /* if the function is a prefix key, i.e. we're changing the definition
  245.         of a prefix key, then they typed a dummy function name, which
  246.         has been translated into a dummy function pointer */
  247.     if (kcmd == &f_cntl_af || kcmd == &f_cntl_xf ||
  248.         kcmd == &f_unarg || kcmd == &f_esc) {
  249.             register CMDFUNC **cfp;
  250.         /* search for an existing binding for the prefix key */
  251.         for (cfp = asciitbl; cfp < &asciitbl[128]; cfp++) {
  252.             if (*cfp == kcmd) {
  253.                 (void)unbindchar(cfp - asciitbl);
  254.                 break;
  255.             }
  256.         }
  257.  
  258.         /* reset the appropriate global prefix variable */
  259.         if (kcmd == &f_cntl_af)
  260.             cntl_a = c;
  261.         if (kcmd == &f_cntl_xf)
  262.             cntl_x = c;
  263.         if (kcmd == &f_unarg)
  264.             reptc = c;
  265.         if (kcmd == &f_esc)
  266.             abortc = c;
  267.     }
  268.     
  269.     if ((c & (CTLA|SPEC|CTLX)) == 0) {
  270.         asciitbl[c] = kcmd;
  271.     } else {
  272.         kbp = kbindtbl;
  273.         while (kbp->k_cmd && kbp->k_code != c)
  274.             kbp++;
  275.         if (kbp->k_cmd) { /* found it, change it in place */
  276.             kbp->k_cmd = kcmd;
  277.         } else {
  278.             if (kbp >= &kbindtbl[NBINDS-1]) {
  279.                 mlforce("[Prefixed binding table full]");
  280.                 return(FALSE);
  281.             }
  282.             kbp->k_code = c;    /* add keycode */
  283.             kbp->k_cmd = kcmd; /* and func pointer */
  284.             ++kbp;        /* and make sure the next is null */
  285.             kbp->k_code = 0;
  286.             kbp->k_cmd = NULL;
  287.         }
  288.     }
  289.  
  290.     return TRUE;
  291. }
  292.  
  293. /* unbindkey:    delete a key from the key binding table    */
  294.  
  295. /* ARGSUSED */
  296. int
  297. unbindkey(f, n)
  298. int f, n;    /* command arguments [IGNORED] */
  299. {
  300.     register int c;        /* command key to unbind */
  301.     char outseq[80];    /* output buffer for keystroke sequence */
  302.     char *kcod2prc();
  303.  
  304.     /* prompt the user to type in a key to unbind */
  305.     mlprompt("Unbind this key sequence: ");
  306.  
  307.     /* get the command sequence to unbind */
  308.     if (clexec) {
  309.         char tok[NSTRING];
  310.         macarg(tok);    /* get the next token */
  311.         c = prc2kcod(tok);
  312.     } else {
  313.         c = kbd_seq();
  314.     }
  315.  
  316.     /* change it to something we can print as well */
  317.     kcod2prc(c, &outseq[0]);
  318.  
  319.     /* and dump it out */
  320.     ostring(outseq);
  321.  
  322.     /* if it isn't bound, bitch */
  323.     if (unbindchar(c) == FALSE) {
  324.         mlforce("[Key not bound]");
  325.         return(FALSE);
  326.     }
  327.     return(TRUE);
  328. }
  329.  
  330. int
  331. unbindchar(c)
  332. int c;        /* command key to unbind */
  333. {
  334.     register KBIND *kbp;    /* pointer into the command table */
  335.     register KBIND *skbp;    /* saved pointer into the command table */
  336.  
  337.     if ((c & (CTLA|SPEC|CTLX)) == 0) {
  338.         asciitbl[c] = NULL;
  339.     } else {
  340.         /* search the table to see if the key exists */
  341.         kbp = kbindtbl;
  342.         while (kbp->k_cmd && kbp->k_code != c)
  343.             kbp++;
  344.  
  345.         /* if it isn't bound, bitch */
  346.         if (kbp->k_cmd == NULL)
  347.             return(FALSE);
  348.  
  349.         /* save the pointer and scan to the end of the table */
  350.         skbp = kbp;
  351.         while (kbp->k_cmd != NULL)
  352.             ++kbp;
  353.         --kbp;        /* backup to the last legit entry */
  354.  
  355.         /* copy the last entry to the current one */
  356.         skbp->k_code = kbp->k_code;
  357.         skbp->k_cmd  = kbp->k_cmd;
  358.  
  359.         /* null out the last one */
  360.         kbp->k_code = 0;
  361.         kbp->k_cmd = NULL;
  362.     }
  363.     return TRUE;
  364. }
  365.  
  366. /* describe bindings bring up a fake buffer and list the key bindings
  367.            into it with view mode            */
  368. /* ARGSUSED */
  369. int
  370. desbind(f, n)
  371. int f,n;
  372. {
  373.         return liststuff("[Binding List]",makebindlist,1,NULL);
  374. }
  375.  
  376. #if    APROP
  377. /* ARGSUSED */
  378. int
  379. apro(f, n)    /* Apropos (List functions that match a substring) */
  380. int f,n;
  381. {
  382.     static char mstring[NSTRING];    /* string to match cmd names to */
  383.         register int    s;
  384.  
  385.  
  386.     s = mlreply("Apropos string: ", mstring, NSTRING - 1);
  387.     if (s != TRUE)
  388.         return(s);
  389.  
  390.         return liststuff("[Binding List]",makebindlist,1,mstring);
  391. }
  392. #endif
  393.  
  394. /* build a binding list (limited or full) */
  395. /* ARGSUSED */
  396. void
  397. makebindlist(dummy, mstring)
  398. int dummy;
  399. char *mstring;        /* match string if partial list, NULL to list all */
  400. {
  401. #if    ST520 & LATTICE
  402. #define    register        
  403. #endif
  404.     register KBIND *kbp;    /* pointer into a key binding table */
  405.     register CMDFUNC **cfp;    /* pointer into the ascii table */
  406.     register NTAB *nptr,*nptr2;    /* pointer into the name table */
  407.     int cpos;        /* current position to use in outseq */
  408.     char outseq[81];    /* output buffer for keystroke sequence */
  409.     int i,pass;
  410.     char *kcod2prc();
  411.  
  412.  
  413.     /* let us know this is in progress */
  414.     mlwrite("[Building binding list]");
  415.  
  416.     /* build the contents of this window, inserting it line by line */
  417.     for (pass = 0; pass < 2; pass++) {
  418.         for (nptr = nametbl; nptr->n_name != NULL; ++nptr) {
  419.  
  420.         /* if we've already described this one, move on */
  421.         if (nptr->n_cmd->c_flags & LISTED)
  422.             continue;
  423.  
  424.         /* try to avoid alphabetizing by the real short names */
  425.         if (pass == 0 && (int)strlen(nptr->n_name) <= 2)
  426.             continue;
  427.  
  428.         /* add in the command name */
  429.         strcpy(outseq,"\"");
  430.         strcat(outseq, nptr->n_name);
  431.         strcat(outseq,"\"");
  432.         cpos = strlen(outseq);
  433.         while (cpos < 32)
  434.             outseq[cpos++] = ' ';
  435.         outseq[cpos] = 0;
  436.         
  437. #if    APROP
  438.         /* if we are executing an apropos command
  439.            and current string doesn't include the search string */
  440.         if (mstring && (strinc(outseq, mstring) == FALSE))
  441.                 continue;
  442. #endif
  443.         /* look in the simple ascii binding table first */
  444.         for(cfp = asciitbl, i = 0; cfp < &asciitbl[128]; cfp++, i++) {
  445.             if (*cfp == nptr->n_cmd) {
  446.                 cpos = kcod2prc(i, &outseq[strlen(outseq)]) -
  447.                     outseq;
  448.                 while(cpos & 7)
  449.                     outseq[cpos++] = ' ';
  450.                 outseq[cpos] = '\0';
  451.             }
  452.         }
  453.         /* then look in the multi-key table */
  454.         for(kbp = kbindtbl; kbp->k_cmd; kbp++) {
  455.             if (kbp->k_cmd == nptr->n_cmd) {
  456.                 cpos = 
  457.                 kcod2prc(kbp->k_code, &outseq[strlen(outseq)]) -
  458.                     outseq;
  459.                 while(cpos & 7)
  460.                     outseq[cpos++] = ' ';
  461.                 outseq[cpos] = '\0';
  462.             }
  463.         }
  464.         /* dump the line */
  465.         addline(curbp,outseq,-1);
  466.  
  467.         cpos = 0;
  468.  
  469.         /* then look for synonyms */
  470.         for (nptr2 = nametbl; nptr2->n_name != NULL; ++nptr2) {
  471.             /* if it's the one we're on, skip */
  472.             if (nptr2 == nptr)
  473.                 continue;
  474.             /* if it's already been listed, skip */
  475.             if (nptr2->n_cmd->c_flags & LISTED)
  476.                 continue;
  477.             /* if it's not a synonym, skip */
  478.             if (nptr2->n_cmd != nptr->n_cmd)
  479.                 continue;
  480.             while (cpos < 8)
  481.                 outseq[cpos++] = ' ';
  482.             outseq[cpos] = '\0';
  483.             strcat(outseq,"\"");
  484.             strcat(outseq,nptr2->n_name);
  485.             strcat(outseq,"\"");
  486.             addline(curbp,outseq,-1);
  487.             cpos = 0;    /* and clear the line */
  488.  
  489.         }
  490.  
  491.         nptr->n_cmd->c_flags |= LISTED; /* mark it as already listed */
  492.         }
  493.     }
  494.  
  495.     for (nptr = nametbl; nptr->n_name != NULL; ++nptr)
  496.         nptr->n_cmd->c_flags &= ~LISTED; /* mark it as unlisted */
  497.  
  498.     mlwrite("");    /* clear the message line */
  499. }
  500.  
  501. #if    APROP
  502. int
  503. strinc(sourc, sub)    /* does source include sub? */
  504. char *sourc;    /* string to search in */
  505. char *sub;    /* substring to look for */
  506. {
  507.     char *sp;    /* ptr into source */
  508.     char *nxtsp;    /* next ptr into source */
  509.     char *tp;    /* ptr into substring */
  510.  
  511.     /* for each character in the source string */
  512.     sp = sourc;
  513.     while (*sp) {
  514.         tp = sub;
  515.         nxtsp = sp;
  516.  
  517.         /* is the substring here? */
  518.         while (*tp) {
  519.             if (*nxtsp++ != *tp)
  520.                 break;
  521.             else
  522.                 tp++;
  523.         }
  524.  
  525.         /* yes, return a success */
  526.         if (*tp == 0)
  527.             return(TRUE);
  528.  
  529.         /* no, onward */
  530.         sp++;
  531.     }
  532.     return(FALSE);
  533. }
  534. #endif
  535.  
  536. #endif /* REBIND */
  537.  
  538.  
  539. /* execute the startup file */
  540.  
  541. int
  542. startup(sfname)
  543. char *sfname;    /* name of startup file  */
  544. {
  545.     char *fname;    /* resulting file name to execute */
  546.  
  547.     /* look up the startup file */
  548.     fname = flook(sfname, FL_HERE_HOME);
  549.  
  550.     /* if it isn't around, don't sweat it */
  551.     if (fname == NULL) {
  552.         mlforce("[Can't find startup file %s]",sfname);
  553.         return(TRUE);
  554.     }
  555.  
  556.     /* otherwise, execute the sucker */
  557.     return(dofile(fname));
  558. }
  559.  
  560. /*    Look up the existence of a file along the normal or PATH
  561.     environment variable. Look first in the HOME directory if
  562.     asked and possible
  563. */
  564.  
  565. char *
  566. flook(fname, hflag)
  567. char *fname;    /* base file name to search for */
  568. int hflag;    /* Look in the HOME environment variable first? */
  569. {
  570.     register char *home;    /* path to home directory */
  571.     register char *path;    /* environmental PATH variable */
  572.     register char *sp;    /* pointer into path spec */
  573.     register int i;        /* index */
  574.     static char fspec[NSTRING];    /* full path spec to search */
  575.     char *getenv();
  576.  
  577.     /* tak care of special cases */
  578.     if (!fname || !fname[0] || isspace(fname[0]))
  579.         return NULL;
  580.     else if (fname[0] == '!')
  581.         return fname;
  582.         
  583.     /* always try the current directory first */
  584.     if (ffropen(fname) == FIOSUC) {
  585.         ffclose();
  586.         return(fname);
  587.     }
  588.  
  589.     if (hflag == FL_HERE)
  590.         return NULL;
  591.  
  592. #if    ENVFUNC
  593.  
  594.     if (hflag) {
  595.         home = getenv("HOME");
  596.         if (home != NULL) {
  597.             /* build home dir file spec */
  598.             strcpy(fspec, home);
  599.             strcat(fspec, "/");
  600.             strcat(fspec, fname);
  601.  
  602.             /* and try it out */
  603.             if (ffropen(fspec) == FIOSUC) {
  604.                 ffclose();
  605.                 return(fspec);
  606.             }
  607.         }
  608.     }
  609.  
  610.     if (hflag == FL_HERE_HOME)
  611.         return NULL;
  612.  
  613. #if PATHLOOK
  614.     /* get the PATH variable */
  615.     path = getenv("PATH");
  616.     if (path != NULL)
  617.         while (*path) {
  618.  
  619.             /* build next possible file spec */
  620.             sp = fspec;
  621.             while (*path && (*path != PATHCHR))
  622.                 *sp++ = *path++;
  623.             *sp++ = '/';
  624.             *sp = 0;
  625.             strcat(fspec, fname);
  626.  
  627.             /* and try it out */
  628.             if (ffropen(fspec) == FIOSUC) {
  629.                 ffclose();
  630.                 return(fspec);
  631.             }
  632.  
  633.             if (*path == PATHCHR)
  634.                 ++path;
  635.         }
  636. #endif
  637. #endif
  638.  
  639.     /* look it up via the old table method */
  640.     for (i=2; i < NPNAMES; i++) {
  641.         strcpy(fspec, pathname[i]);
  642.         strcat(fspec, fname);
  643.  
  644.         /* and try it out */
  645.         if (ffropen(fspec) == FIOSUC) {
  646.             ffclose();
  647.             return(fspec);
  648.         }
  649.     }
  650.  
  651.  
  652.     return NULL;    /* no such luck */
  653. }
  654.  
  655. /* translate a 10-bit keycode to its printable name (like "M-j")  */
  656. char *
  657. kcod2prc(c, seq)
  658. int c;        /* sequence to translate */
  659. char *seq;    /* destination string for sequence */
  660. {
  661.     char *ptr;    /* pointer into current position in sequence */
  662.  
  663.     ptr = seq;
  664.  
  665.     /* apply cntl_a sequence if needed */
  666.     if (c & CTLA) {
  667.         *ptr++ = '^';
  668.         *ptr++ = 'A';
  669.         *ptr++ = '-';
  670.     }
  671.  
  672.     /* apply ^X sequence if needed */
  673.     if (c & CTLX) {
  674.         *ptr++ = '^';
  675.         *ptr++ = 'X';
  676.         *ptr++ = '-';
  677.     }
  678.  
  679.     /* apply SPEC sequence if needed */
  680.     if (c & SPEC) {
  681.         *ptr++ = 'F';
  682.         *ptr++ = 'N';
  683.         *ptr++ = '-';
  684.     }
  685.     
  686.     c = kcod2key(c);
  687.  
  688.     /* apply control sequence if needed */
  689.     if (iscntrl(c)) {
  690.         *ptr++ = '^';
  691.         c = toalpha(c);
  692.     }
  693.  
  694.     /* and output the final sequence */
  695.  
  696.     if (c == ' ') {
  697.         *ptr++ = '<';
  698.         *ptr++ = 's';
  699.         *ptr++ = 'p';
  700.         *ptr++ = '>';
  701.     } else if (c == '\t') {
  702.         *ptr++ = '<';
  703.         *ptr++ = 't';
  704.         *ptr++ = 'a';
  705.         *ptr++ = 'b';
  706.         *ptr++ = '>';
  707.     } else {
  708.         *ptr++ = c;
  709.     }
  710.     *ptr = 0;    /* terminate the string */
  711.     return ptr;
  712. }
  713.  
  714.  
  715. /* kcod2fnc:  translate a 10-bit keycode to a function pointer */
  716. /*    (look a key binding up in the binding table)        */
  717. CMDFUNC *
  718. kcod2fnc(c)
  719. int c;    /* key to find what is bound to it */
  720. {
  721.     register KBIND *kbp;
  722.  
  723.     if ((c & (CTLA|SPEC|CTLX)) == 0) {
  724.         return asciitbl[c];
  725.     } else {
  726.         kbp = kbindtbl;
  727.         while (kbp->k_cmd && kbp->k_code != c)
  728.             kbp++;
  729.         return kbp->k_cmd;
  730.     }
  731. }
  732.  
  733.  
  734. /* fnc2engl: translate a function pointer to the english name for 
  735.         that function
  736. */
  737.  
  738. char *
  739. fnc2engl(cfp)
  740. CMDFUNC *cfp;    /* ptr to the requested function to bind to */
  741. {
  742.     register NTAB *nptr;    /* pointer into the name table */
  743.  
  744.     /* skim through the table, looking for a match */
  745.     for (nptr = nametbl; nptr->n_cmd; nptr++) {
  746.         if (nptr->n_cmd == cfp) {
  747.             return(nptr->n_name);
  748.         }
  749.     }
  750.     return NULL;
  751. }
  752.  
  753. /* fnc2key: translate a function pointer to a simple key that is bound
  754.         to that function
  755. */
  756.  
  757. int
  758. fnc2key(cfp)
  759. CMDFUNC *cfp;    /* ptr to the requested function to bind to */
  760. {
  761.     register int i;
  762.  
  763.     for(i = 0; i < 128; i++) {
  764.         if (cfp == asciitbl[i])
  765.             return i;
  766.     }
  767.     return -1;
  768. }
  769.  
  770. #if NEEDED
  771. /* translate a function pointer to its associated flags */
  772. fnc2flags(func)
  773. CMDFUNC *cfp;    /* ptr to the requested function to bind to */
  774. {
  775.     register NTAB *nptr;    /* pointer into the name binding table */
  776.  
  777.     /* skim through the table, looking for a match */
  778.     nptr = nametbl;
  779.     while (nptr->n_cmd != NULL) {
  780.         if (nptr->n_cmd == cfp) {
  781.             return nptr->n_flags;
  782.         }
  783.         ++nptr;
  784.     }
  785.     return NONE;
  786. }
  787. #endif
  788.  
  789.  
  790. /* engl2fnc: match name to a function in the names table
  791.     translate english name to function pointer
  792.           return any match or NULL if none
  793.  */
  794. CMDFUNC *
  795. engl2fnc(fname)
  796. char *fname;    /* name to attempt to match */
  797. {
  798.     register NTAB *nptr;    /* pointer to entry in name binding table */
  799.  
  800.     /* scan through the table, returning any match */
  801.     nptr = nametbl;
  802.     while (nptr->n_cmd != NULL) {
  803.         if (strcmp(fname, nptr->n_name) == 0) {
  804.             return nptr->n_cmd;
  805.         }
  806.         ++nptr;
  807.     }
  808.     return NULL;
  809. }
  810.  
  811. /* prc2kcod: translate printable code to 10 bit keycode */
  812. int 
  813. prc2kcod(k)
  814. char *k;        /* name of key to translate to Command key form */
  815. {
  816.     register int c;    /* key sequence to return */
  817.  
  818.     /* parse it up */
  819.     c = 0;
  820.  
  821.     /* first, the CTLA prefix */
  822.     if (*k == '^' && *(k+1) == toalpha(cntl_a) && *(k+2) == '-') {
  823.         c = CTLA;
  824.         k += 3;
  825.     }
  826.  
  827.     /* next the function prefix */
  828.     if (*k == 'F' && *(k+1) == 'N' && *(k+2) == '-') {
  829.         c |= SPEC;
  830.         k += 3;
  831.     }
  832.  
  833.     /* control-x as well... (but not with FN) */
  834.     if (*k == '^' && *(k+1) == toalpha(cntl_x) && 
  835.                 *(k+2) == '-' && !(c & SPEC)) {
  836.         c |= CTLX;
  837.         k += 3;
  838.     }
  839.  
  840.     /* a control char? */
  841.     if (*k == '^' && *(k+1) != 0) {
  842.         ++k;
  843.         c |= *k;
  844.         if (islower(c)) c = toupper(c);
  845.         c = tocntrl(c);
  846.     } else if (!strcmp(k,"<sp>")) {
  847.         c |= ' ';
  848.     } else if (!strcmp(k,"<tab>")) {
  849.         c |= '\t';
  850.     } else {
  851.         c |= *k;
  852.     }
  853.     return c;
  854. }
  855.  
  856. #if ! SMALLER
  857.  
  858. /* translate printable code (like "M-r") to english command name */
  859. char *
  860. prc2engl(skey)    /* string key name to binding name.... */
  861. char *skey;    /* name of key to get binding for */
  862. {
  863.     char *bindname;
  864.  
  865.     bindname = fnc2engl(kcod2fnc(prc2kcod(skey)));
  866.     if (bindname == NULL)
  867.         bindname = "ERROR";
  868.  
  869.     return bindname;
  870. }
  871. #endif
  872.  
  873. /* get an english command name from the user. Command completion means
  874.  * that pressing a <SPACE> will attempt to complete an unfinished command
  875.  * name if it is unique.
  876.  */
  877. char *
  878. kbd_engl()
  879. {
  880.     int status;
  881.     char *buf;
  882.  
  883.  
  884.     status = kbd_engl_stat(&buf);
  885.     if (status == SORTOFTRUE) /* something was tungetc'ed */
  886.         (void)tgetc();
  887.     if (status == TRUE)
  888.         return buf;
  889.     return NULL;
  890. }
  891.  
  892. /* *bufp only valid if return is TRUE */
  893. int
  894. kbd_engl_stat(bufp)
  895. char **bufp;
  896. {
  897. #if    ST520 & LATTICE
  898. #define register        
  899. #endif
  900.     register int c;
  901.     register int cpos;    /* current column on screen output */
  902.     register char *sp;    /* pointer to string for output */
  903.     register NTAB *nbp;    /* first ptr to entry in name binding table */
  904.     register NTAB *cnbp;    /* current ptr to entry in name binding table */
  905.     register NTAB *lnbp;    /* last ptr to entry in name binding table */
  906.     static char buf[NLINE]; /* buffer to hold tentative command name */
  907.  
  908.     cpos = 0;
  909.  
  910.     *bufp = NULL;
  911.  
  912.     /* if we are executing a command line just get the next arg and return */
  913.     if (clexec) {
  914.         if (macarg(buf) != TRUE) {
  915.             return FALSE;
  916.         }
  917.         *bufp = buf;
  918.         return TRUE;
  919.     }
  920.  
  921.     lineinput = TRUE;
  922.  
  923.     /* build a name string from the keyboard */
  924.     while (TRUE) {
  925.         c = tgetc();
  926.  
  927.         if (cpos == 0) {
  928.             if (isbackspace(c) ||
  929.                 c == kcod2key(abortc) ||
  930.                 c == kcod2key(killc) ||
  931.                 c == '\r' ||
  932.                 c == '\n' ||
  933.                 islinespecchar(c) ) {
  934.                 tungetc(c);
  935.                 return SORTOFTRUE;
  936.             }
  937.         }
  938.  
  939.         if (c == '\r' || c == '\n') {
  940.             buf[cpos] = 0;
  941.             lineinput = FALSE;
  942.             *bufp = buf;
  943.             return TRUE;
  944.  
  945.         } else if (c == kcod2key(abortc)) {    /* Bell, abort */
  946.             buf[0] = '\0';
  947.             lineinput = FALSE;
  948.             return FALSE;
  949.  
  950.         } else if (isbackspace(c)) {
  951.             TTputc('\b');
  952.             TTputc(' ');
  953.             TTputc('\b');
  954.             --ttcol;
  955.             --cpos;
  956.             TTflush();
  957.  
  958.         } else if (c == kcod2key(killc)) {    /* ^U, kill */
  959.             while (cpos != 0) {
  960.                 TTputc('\b');
  961.                 TTputc(' ');
  962.                 TTputc('\b');
  963.                 --cpos;
  964.                 --ttcol;
  965.             }
  966.             TTflush();
  967.             tungetc(c);
  968.             return SORTOFTRUE;
  969.  
  970.         } /* else... */
  971. /* the following mess causes the command to terminate if:
  972.     we've got a space
  973.         -or-
  974.     we're in the first few chars and we're switching from punctuation
  975.     to alphanumerics, or vice-versa.  oh yeah -- '!' is considered
  976.     alphanumeric today.
  977.     All this allows things like:
  978.         : e#
  979.         : !ls
  980.         : q!
  981.     to work properly.
  982.     If we pass this "if" with c != ' ', then c is ungotten below,
  983.     so it can be picked up by the commands argument getter later.
  984. */
  985.         else if (c == ' ' || (cpos > 0 && cpos < 3 &&
  986.                  ((!ispunct(c) &&  ispunct(buf[cpos-1])) ||
  987.        ((c != '!' && ispunct(c)) &&
  988.             (buf[cpos-1] == '!' || !ispunct(buf[cpos-1]))) )
  989.                           )
  990.             ) {
  991. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  992.     /* attempt a completion */
  993.     buf[cpos] = 0;        /* terminate it for us */
  994.     nbp = nametbl;    /* scan for matches */
  995.     while (nbp->n_cmd != NULL) {
  996.         if (strncmp(buf, nbp->n_name, strlen(buf)) == 0) {
  997.             /* a possible match! exact? no more than one? */
  998.             if (strcmp(buf, nbp->n_name) == 0 || /* exact? */
  999.                 (nbp + 1)->n_cmd == NULL ||
  1000.                 strncmp(buf, (nbp+1)->n_name, strlen(buf)) != 0)
  1001.             {
  1002.                 /* exact or only one like it.  print it */
  1003.                 sp = nbp->n_name + cpos;
  1004.                 while (*sp)
  1005.                     TTputc(*sp++);
  1006.                 TTflush();
  1007.                 if (c != ' ')  /* put it back */
  1008.                     tungetc(c);
  1009.                 lineinput = FALSE;
  1010.                 /* return nbp->n_name; */
  1011.                 strncpy(buf,nbp->n_name,NLINE);
  1012.                 *bufp = buf;
  1013.                 return TRUE;
  1014.             } else {
  1015. /* << << << << << << << << << << << << << << << << << */
  1016.     /* try for a partial match against the list */
  1017.  
  1018.     /* first scan down until we no longer match the current input */
  1019.     lnbp = (nbp + 1);
  1020.     while ((lnbp+1)->n_cmd != NULL) {
  1021.         if (strncmp(buf, (lnbp+1)->n_name, strlen(buf)) != 0)
  1022.             break;
  1023.         ++lnbp;
  1024.     }
  1025.  
  1026.     /* and now, attempt to partial complete the string, char at a time */
  1027.     while (TRUE) {
  1028.         /* add the next char in */
  1029.         buf[cpos] = nbp->n_name[cpos];
  1030.  
  1031.         /* scan through the candidates */
  1032.         cnbp = nbp + 1;
  1033.         while (cnbp <= lnbp) {
  1034.             if (cnbp->n_name[cpos] != buf[cpos])
  1035.                 goto onward;
  1036.             ++cnbp;
  1037.         }
  1038.  
  1039.         /* add the character */
  1040.         TTputc(buf[cpos++]);
  1041.     }
  1042. /* << << << << << << << << << << << << << << << << << */
  1043.             }
  1044.         }
  1045.         ++nbp;
  1046.     }
  1047.  
  1048.     /* no match.....beep and onward */
  1049.     TTbeep();
  1050. onward:;
  1051.     TTflush();
  1052. /* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
  1053.         } else {
  1054.             if (cpos < NLINE-1 && isprint(c)) {
  1055.                 buf[cpos++] = c;
  1056.                 TTputc(c);
  1057.             }
  1058.  
  1059.             ++ttcol;
  1060.             TTflush();
  1061.         }
  1062.     }
  1063. }
  1064.  
  1065.