This is bind.c in view mode; [Download] [Up]
/* This file is for functions having to do with key bindings,
* descriptions, help commands and startup file.
*
* written 11-feb-86 by Daniel Lawrence
*
* $Header: /home/tom/src/vile/RCS/bind.c,v 1.147 1997/03/01 01:33:42 tom Exp $
*
*/
#include "estruct.h"
#include "edef.h"
#include "nefunc.h"
#define SHORT_CMD_LEN 4 /* command names longer than this are preferred
over those shorter. e.g. display "quit"
instead of "q" if possible */
extern const int nametblsize;
static KBIND * kcode2kbind ( int code );
#if OPT_CASELESS
#define Strcmp(s,d) cs_strcmp(case_insensitive, s, d)
#define StrNcmp(s,d,len) cs_strncmp(case_insensitive, s, d, len)
#else
#define Strcmp(s,d) strcmp(s, d)
#define StrNcmp(s,d,len) strncmp(s, d, len)
#endif
#if OPT_REBIND
#define isSpecialCmd(k) \
( (k == &f_cntl_a_func)\
||(k == &f_cntl_x_func)\
||(k == &f_poundc_func)\
||(k == &f_unarg_func)\
||(k == &f_esc_func)\
)
static char * kcod2prc (int c, char *seq);
static int install_bind (int c, const CMDFUNC *kcmd, const CMDFUNC **oldfunc);
static int key_to_bind ( const CMDFUNC *kcmd );
static int rebind_key ( int c, const CMDFUNC *kcmd );
static int strinc (char *sourc, char *sub);
static int unbindchar ( int c );
static int update_binding_list ( BUFFER *bp );
static void makebindlist (LIST_ARGS);
#endif /* OPT_REBIND */
#if OPT_EVAL || OPT_REBIND
static const NTAB * fnc2ntab ( const CMDFUNC *cfp );
static int prc2kcod ( const char *kk );
#endif
#if OPT_REBIND
static KBIND *KeyBindings = kbindtbl;
#endif
/*----------------------------------------------------------------------------*/
int
no_such_function(const char * fnp)
{
mlforce("[No such function \"%s\"]", fnp != 0 ? fnp : "");
return FALSE;
}
/* give me some help!!!! bring up a buffer and read the help file into it */
/* ARGSUSED */
int
help(int f, int n)
{
register BUFFER *bp; /* buffer pointer to help */
const char *fname; /* ptr to file returned by flook() */
int alreadypopped;
/* first check if we are already here */
bp = bfind(HELP_BufName, BFSCRTCH);
if (bp == NULL)
return FALSE;
if (bp->b_active == FALSE) { /* never been used */
fname = flook(helpfile, FL_ANYWHERE|FL_READABLE);
if (fname == NULL) {
mlforce("[Sorry, can't find the help information]");
(void)zotbuf(bp);
return(FALSE);
}
alreadypopped = (bp->b_nwnd != 0);
/* and read the stuff in */
if (readin(fname, 0, bp, TRUE) == FALSE ||
popupbuff(bp) == FALSE) {
(void)zotbuf(bp);
return(FALSE);
}
set_bname(bp, HELP_BufName);
set_rdonly(bp, fname, MDVIEW);
make_local_b_val(bp,MDIGNCASE); /* easy to search, */
set_b_val(bp,MDIGNCASE,TRUE);
b_set_scratch(bp);
if (!alreadypopped)
shrinkwrap();
}
if (!swbuffer(bp))
return FALSE;
if (help_at >= 0) {
if (!gotoline(TRUE, help_at))
return FALSE;
mlwrite("[Type '1G' to return to start of help information]");
help_at = -1; /* until zotbuf is called, we let normal
DOT tracking keep our position */
}
return TRUE;
}
#if OPT_REBIND
#if OPT_TERMCHRS
/* patch: this table and the corresponding initializations should be
* generated by 'mktbls'. In any case, the table must be sorted to use
* name-completion on it.
*/
static const struct {
const char *name;
int *value;
char how_to;
} TermChrs[] = {
{"backspace", &backspc, 's'},
{"interrupt", &intrc, 's'},
{"line-kill", &killc, 's'},
{"name-complete", &name_cmpl, 0},
{"quote-next", "ec, 0},
{"start-output", &startc, 's'},
{"stop-output", &stopc, 's'},
{"suspend", &suspc, 's'},
{"test-completions", &test_cmpl, 0},
{"word-kill", &wkillc, 's'},
{0}
};
/*----------------------------------------------------------------------------*/
/* list the current chrs into the current buffer */
/* ARGSUSED */
static void
makechrslist(int dum1, void *ptr)
{
register int i;
char temp[NLINE];
bprintf("--- Terminal Character Settings %*P\n", term.t_ncol-1, '-');
for (i = 0; TermChrs[i].name != 0; i++) {
bprintf("\n%s = %s",
TermChrs[i].name,
kcod2prc(*(TermChrs[i].value), temp));
}
}
/*
* Find a special-character definition, given the name
*/
static int
chr_lookup(char *name)
{
register int j;
for (j = 0; TermChrs[j].name != 0; j++)
if (!strcmp(name, TermChrs[j].name))
return j;
return -1;
}
/*
* The 'chr_complete()' and 'chr_eol()' functions are invoked from
* 'kbd_reply()' to setup the mode-name completion and query displays.
*/
static int
chr_complete(int c, char *buf, int *pos)
{
return kbd_complete(FALSE, c, buf, pos, (const char *)&TermChrs[0],
sizeof(TermChrs[0]));
}
static int
/*ARGSUSED*/
chr_eol(char * buffer, int cpos, int c, int eolchar)
{
return isspace(c);
}
#if OPT_UPBUFF
/* ARGSUSED */
static int
update_termchrs(BUFFER *bp)
{
return show_termchrs(FALSE,1);
}
#endif
/* ARGSUSED */
int
set_termchrs(int f, int n)
{
register int s, j;
char name[NLINE];
int c;
/* get the table-entry */
*name = EOS;
if ((s = kbd_reply("Terminal setting: ", name, sizeof(name), chr_eol,
' ', 0, chr_complete)) == TRUE) {
j = chr_lookup(name);
switch (TermChrs[j].how_to) {
case 's':
default:
c = key_to_bind((CMDFUNC *)0);
if (c < 0)
return(FALSE);
*(TermChrs[j].value) = c;
break;
}
update_scratch(TERMINALCHARS_BufName, update_termchrs);
}
return s;
}
/* ARGSUSED */
int
show_termchrs(int f, int n)
{
return liststuff(TERMINALCHARS_BufName, FALSE, makechrslist, 0, (void *)0);
}
#endif /* OPT_TERMCHRS */
static void
ostring( /* output a string of output characters */
char *s) /* string to output */
{
if (discmd)
kbd_puts(s);
}
/* bindkey: add a new key to the key binding table */
/* ARGSUSED */
int
bindkey(int f, int n)
{
register const CMDFUNC *kcmd; /* ptr to the requested function to bind to */
char cmd[NLINE];
char *fnp;
/* prompt the user to type in a key to bind */
/* and get the function name to bind it to */
fnp = kbd_engl("Bind function whose full name is: ", cmd);
if (fnp == NULL || (kcmd = engl2fnc(fnp)) == NULL) {
return no_such_function(fnp);
}
return rebind_key(key_to_bind(kcmd), kcmd);
}
/*
* Prompt-for and return the key-code to bind.
*/
static int
key_to_bind(register const CMDFUNC *kcmd)
{
char outseq[NLINE]; /* output buffer for keystroke sequence */
register int c;
mlprompt("...to keyboard sequence (type it exactly): ");
/* get the command sequence to bind */
if (clexec) {
char tok[NSTRING];
macarg(tok); /* get the next token */
c = prc2kcod(tok);
} else {
/* perhaps we only want a single key, not a sequence */
/* (see more comments below) */
if (isSpecialCmd(kcmd))
c = keystroke();
else
c = kbd_seq();
}
if (c >= 0) {
/* change it to something we can print as well */
ostring(kcod2prc(c, outseq));
hst_append(outseq, FALSE);
} else {
mlforce("[Not a proper key-sequence]");
}
return c;
}
/*
* Given a key-code and a command-function pointer, rebind the key-code to
* the command-function.
*/
static int
rebind_key (
register int c,
register const CMDFUNC *kcmd)
{
static const CMDFUNC ignored = { unimpl }, *old = &ignored;
return install_bind (c, kcmd, &old);
}
/*
* Prefix-keys can be only bound to one value. This procedure tests the
* argument 'kcmd' to see if it is a prefix key, and if so, unbinds the
* key, and sets the corresponding global variable to the new value.
* The calling procedure will then do the binding per se.
*/
static void
reset_prefix (
register int c,
register const CMDFUNC *kcmd)
{
if (isSpecialCmd(kcmd)) {
register int j;
/* search for an existing binding for the prefix key */
if ((j = fnc2kcod(kcmd)) >= 0)
(void)unbindchar(j);
/* reset the appropriate global prefix variable */
if (kcmd == &f_cntl_a_func)
cntl_a = c;
if (kcmd == &f_cntl_x_func)
cntl_x = c;
if (kcmd == &f_poundc_func)
poundc = c;
if (kcmd == &f_unarg_func)
reptc = c;
if (kcmd == &f_esc_func)
abortc = c;
}
}
/*
* Bind a command-function pointer to a given key-code (saving the old
* value of the function-pointer via an pointer given by the caller).
*/
static int
install_bind (
register int c,
register const CMDFUNC *kcmd,
const CMDFUNC **oldfunc)
{
register KBIND *kbp; /* pointer into a binding table */
if (c < 0)
return FALSE; /* not a legal key-code */
/* if the function is a prefix key, i.e. we're changing the definition
of a prefix key, then they typed a dummy function name, which
has been translated into a dummy function pointer */
*oldfunc = kcod2fnc(c);
reset_prefix(-1, *oldfunc);
reset_prefix(c, kcmd);
if (!isspecial(c)) {
asciitbl[c] = (CMDFUNC *)kcmd;
} else {
if ((kbp = kcode2kbind(c)) != 0) { /* change it in place */
kbp->k_cmd = kcmd;
} else {
if ((kbp = typealloc(KBIND)) == 0) {
return no_memory("Key-Binding");
}
kbp->k_link = KeyBindings;
kbp->k_code = (short)c; /* add keycode */
kbp->k_cmd = kcmd; /* and func pointer */
KeyBindings = kbp;
}
}
update_scratch(BINDINGLIST_BufName, update_binding_list);
return(TRUE);
}
/* unbindkey: delete a key from the key binding table */
/* ARGSUSED */
int
unbindkey(int f, int n)
{
register int c; /* command key to unbind */
char outseq[NLINE]; /* output buffer for keystroke sequence */
/* prompt the user to type in a key to unbind */
mlprompt("Unbind this key sequence: ");
/* get the command sequence to unbind */
if (clexec) {
char tok[NSTRING];
macarg(tok); /* get the next token */
c = prc2kcod(tok);
if (c < 0) {
mlforce("[Illegal key-sequence \"%s\"]",tok);
return FALSE;
}
} else {
c = kbd_seq();
if (c < 0) {
mlforce("[Not a bindable key-sequence]");
return(FALSE);
}
}
/* change it to something we can print as well */
ostring(kcod2prc(c, outseq));
/* if it isn't bound, bitch */
if (unbindchar(c) == FALSE) {
mlforce("[Key not bound]");
return(FALSE);
}
update_scratch(BINDINGLIST_BufName, update_binding_list);
return(TRUE);
}
static int
unbindchar(int c) /* command key to unbind */
{
register KBIND *kbp; /* pointer into the command table */
register KBIND *skbp; /* saved pointer into the command table */
if (!isspecial(c)) {
asciitbl[c] = NULL;
} else {
/* search the table to see if the key exists */
if ((kbp = kcode2kbind(c)) == 0)
return(FALSE);
/* save the pointer and scan to the end of the table */
skbp = kbp;
while (kbp->k_cmd != NULL)
++kbp;
--kbp; /* backup to the last legit entry */
/* copy the last entry to the current one */
skbp->k_code = kbp->k_code;
skbp->k_cmd = kbp->k_cmd;
/* null out the last one */
kbp->k_code = 0;
kbp->k_cmd = NULL;
}
return TRUE;
}
/* describe bindings bring up a fake buffer and list the key bindings
into it with view mode */
/* remember whether we last did "apropos" or "describe-bindings" */
static char *last_apropos_string;
static CMDFLAGS last_whichcmds;
static append_to_binding_list;
/* ARGSUSED */
static int
update_binding_list(BUFFER *bp)
{
return liststuff(BINDINGLIST_BufName, append_to_binding_list,
makebindlist, (int)last_whichcmds, (void *)last_apropos_string);
}
/* ARGSUSED */
int
desbind(int f, int n)
{
last_apropos_string = (char *)0;
last_whichcmds = 0;
return update_binding_list((BUFFER *)0);
}
/* ARGSUSED */
int
desmotions(int f, int n)
{
last_apropos_string = (char *)0;
last_whichcmds = MOTION;
return update_binding_list((BUFFER *)0);
}
/* ARGSUSED */
int
desopers(int f, int n)
{
last_apropos_string = (char *)0;
last_whichcmds = OPER;
return update_binding_list((BUFFER *)0);
}
/* ARGSUSED */
int
desapro(int f, int n) /* Apropos (List functions that match a substring) */
{
register int s;
static char mstring[NSTRING]; /* string to match cmd names to */
s = mlreply("Apropos string: ", mstring, sizeof(mstring));
if (s != TRUE)
return(s);
last_apropos_string = mstring;
last_whichcmds = 0;
return update_binding_list((BUFFER *)0);
}
static char described_cmd[NLINE+1]; /* string to match cmd names to */
/* ARGSUSED */
int
desfunc(int f, int n) /* describe-function */
{
register int s;
char *fnp;
/* force an exact match by strinc() later on from makefuncdesc() */
described_cmd[0] = '^';
fnp = kbd_engl("Describe function whose full name is: ",
described_cmd+1);
if (fnp == NULL || engl2fnc(fnp) == NULL) {
return no_such_function(fnp);
}
last_apropos_string = described_cmd;
last_whichcmds = 0;
append_to_binding_list = TRUE;
s = update_binding_list((BUFFER *)0);
append_to_binding_list = FALSE;
return s;
}
/* ARGSUSED */
int
deskey(int f, int n) /* describe the command for a certain key */
{
register int c; /* key to describe */
char outseq[NSTRING]; /* output buffer for command sequence */
const NTAB *nptr; /* name table pointer */
int s;
/* prompt the user to type us a key to describe */
mlprompt("Describe the function bound to this key sequence: ");
/* get the command sequence to describe
change it to something we can print as well */
/* check to see if we are executing a command line */
if (clexec) {
char tok[NSTRING];
macarg(tok); /* get the next token */
c = prc2kcod(tok);
if (c < 0) {
mlforce("[Illegal key-sequence \"%s\"]",tok);
return(FALSE);
}
} else {
c = kbd_seq();
if (c < 0) {
mlforce("[Not a bindable key-sequence]");
return(FALSE);
}
}
(void)kcod2prc(c, outseq);
hst_append(outseq, EOS); /* cannot replay this, but can see it */
/* find the right ->function */
if ((nptr = fnc2ntab(kcod2fnc(c))) == NULL) {
mlwrite("Key sequence '%s' is not bound to anything.",
outseq);
return TRUE;
}
/* describe it */
described_cmd[0] = '^';
(void)strcpy(described_cmd + 1, nptr->n_name);
last_apropos_string = described_cmd;
last_whichcmds = 0;
append_to_binding_list = TRUE;
s = update_binding_list((BUFFER *)0);
append_to_binding_list = FALSE;
mlwrite("Key sequence '%s' is bound to function \"%s\"",
outseq, nptr->n_name);
return s;
}
/* returns a name in double-quotes */
static char *
quoted(char *dst, char *src)
{
return strcat(strcat(strcpy(dst, "\""), src), "\"");
}
/* returns the number of columns used by the given string */
static int
converted_len(register char *buffer)
{
register int len = 0, c;
while ((c = *buffer++) != EOS) {
if (c == '\t')
len |= 7;
len++;
}
return len;
}
/* force the buffer to a tab-stop if needed */
static char *
to_tabstop(char *buffer)
{
register int cpos = converted_len(buffer);
if (cpos & 7)
(void)strcat(buffer, "\t");
return strend(buffer);
}
/* convert a key binding, padding to the next multiple of 8 columns */
static void
convert_kcode(int c, char *buffer)
{
(void)kcod2prc(c, to_tabstop(buffer));
}
/* fully describe a function into the current buffer, given a pointer to
* its name table entry */
static int
makefuncdesc(int j, char *listed)
{
register KBIND *kbp; /* pointer into a key binding table */
int i;
const CMDFUNC *cmd = nametbl[j].n_cmd;
char outseq[NLINE]; /* output buffer for keystroke sequence */
/* add in the command name */
(void)quoted(outseq, nametbl[j].n_name);
while (converted_len(outseq) < 32)
(void)strcat(outseq, "\t");
/* look in the simple ascii binding table first */
for (i = 0; i < N_chars; i++)
if (asciitbl[i] == cmd)
convert_kcode(i, outseq);
/* then look in the multi-key table */
#if OPT_REBIND
for (kbp = KeyBindings; kbp != kbindtbl; kbp = kbp->k_link) {
if (kbp->k_cmd == cmd)
convert_kcode(kbp->k_code, outseq);
}
#endif
for (kbp = kbindtbl; kbp->k_cmd; kbp++)
if (kbp->k_cmd == cmd)
convert_kcode(kbp->k_code, outseq);
/* dump the line */
if (!addline(curbp,outseq,-1))
return FALSE;
/* then look for synonyms */
(void)strcpy(outseq, " or\t");
for (i = 0; nametbl[i].n_name != 0; i++) {
/* if it's the one we're on, skip */
if (i == j)
continue;
/* if it's already been listed, skip */
if (listed[i])
continue;
/* if it's not a synonym, skip */
if (nametbl[i].n_cmd != cmd)
continue;
(void)quoted(outseq+5, nametbl[i].n_name);
if (!addline(curbp,outseq,-1))
return FALSE;
}
#if OPT_ONLINEHELP
if (cmd->c_help && cmd->c_help[0])
(void)lsprintf(outseq," (%s %s )",
(cmd->c_flags & MOTION) ? "motion: " :
(cmd->c_flags & OPER) ? "operator: " : "",
cmd->c_help);
else
(void)lsprintf(outseq," ( no help for this command )");
if (!addline(curbp,outseq,-1))
return FALSE;
if (cmd->c_flags & GLOBOK) {
if (!addline(curbp," (may follow global command)",-1))
return FALSE;
}
#endif
/* blank separator */
if (!addline(curbp,"",-1))
return FALSE;
return TRUE;
}
/* build a binding list (limited or full) */
/* ARGSUSED */
static void
makebindlist(
int whichmask,
void *mstring) /* match string if partial list, NULL to list all */
{
int pass;
int j;
int ok = TRUE; /* reset if out-of-memory, etc. */
char *listed = calloc(sizeof(char), (ALLOC_T)nametblsize);
if (listed == 0) {
(void)no_memory(BINDINGLIST_BufName);
return;
}
/* let us know this is in progress */
mlwrite("[Building binding list]");
/* build the contents of this window, inserting it line by line */
for (pass = 0; pass < 2; pass++) {
for (j = 0; nametbl[j].n_name != 0; j++) {
/* if we've already described this one, move on */
if (listed[j])
continue;
/* are we interested in this type of command? */
if (whichmask && !(nametbl[j].n_cmd->c_flags & whichmask))
continue;
/* try to avoid alphabetizing by the real short names */
if (pass == 0 && (int)strlen(nametbl[j].n_name) <= SHORT_CMD_LEN)
continue;
/* if we are executing an apropos command
and current string doesn't include the search string */
if (mstring
&& (strinc(nametbl[j].n_name, (char *)mstring) == FALSE))
continue;
ok = makefuncdesc(j, listed);
if (!ok)
break;
listed[j] = TRUE; /* mark it as already listed */
}
}
if (ok)
mlerase(); /* clear the message line */
free(listed);
}
/* much like the "standard" strstr, but if the substring starts
with a '^', we discard it and force an exact match. */
static int
strinc( /* does source include sub? */
char *sourc, /* string to search in */
char *sub) /* substring to look for */
{
char *sp; /* ptr into source */
char *nxtsp; /* next ptr into source */
char *tp; /* ptr into substring */
int exact = (*sub == '^');
if (exact)
sub++;
/* for each character in the source string */
sp = sourc;
while (*sp) {
tp = sub;
nxtsp = sp;
/* is the substring here? */
while (*tp) {
if (*nxtsp++ != *tp)
break;
tp++;
}
if ((*tp == EOS) && (!exact || *nxtsp == EOS))
return(TRUE);
if (exact) /* we only get one chance */
break;
/* no, onward */
sp++;
}
return(FALSE);
}
#endif /* OPT_REBIND */
/* execute the startup file */
int
startup(
const char *sfname) /* name of startup file */
{
const char *fname; /* resulting file name to execute */
/* look up the startup file */
fname = flook(sfname, (FL_HERE|FL_HOME)|FL_READABLE);
/* if it isn't around, don't sweat it */
if (fname == NULL) {
mlforce("[Can't find startup file %s]",sfname);
return(TRUE);
}
/* otherwise, execute the sucker */
return(dofile(fname));
}
/* Look up the existence of a file along the normal or PATH
environment variable. Look first in the HOME directory if
asked and possible
*/
const char *
flook(
const char *fname, /* base file name to search for */
int hflag) /* Look in the HOME environment variable first? */
{
register char *home; /* path to home directory */
#if ENVFUNC && OPT_PATHLOOKUP
register const char *path; /* environmental PATH variable */
#endif
static char fspec[NSTRING]; /* full path spec to search */
#if SYS_VMS
register char *sp; /* pointer into path spec */
static TBUFF *myfiles;
#endif
int mode = (hflag & (FL_EXECABLE|FL_WRITEABLE|FL_READABLE));
/* take care of special cases */
if (!fname || !fname[0] || isspace(fname[0]))
return NULL;
else if (isShellOrPipe(fname))
return fname;
if (hflag & FL_HERE) {
if (ffaccess(fname, mode)) {
return(fname);
}
}
#if ENVFUNC
if (hflag & FL_HOME) {
home = getenv("HOME");
if (home != NULL) {
/* try home dir file spec */
if (ffaccess(pathcat(fspec,home,fname), mode)) {
return(fspec);
}
}
}
#endif /* ENVFUNC */
if (hflag & FL_EXECDIR) { /* is it where we found the executable? */
if (exec_pathname
&& exec_pathname[0] != EOS
&& ffaccess(pathcat(fspec, exec_pathname, fname), mode))
return(fspec);
}
if (hflag & FL_TABLE) {
/* then look it up via the table method */
path = startup_path;
while ((path = parse_pathlist(path, fspec)) != 0) {
if (ffaccess(pathcat(fspec, fspec, fname), mode)) {
return(fspec);
}
}
}
if (hflag & FL_PATH) {
#if ENVFUNC
#if OPT_PATHLOOKUP
/* then look along $PATH */
#if SYS_VMS
/* On VAX/VMS, the PATH environment variable is only the
* current-dir. Fake up an acceptable alternative.
*/
if (!tb_length(myfiles)) {
char mypath[NFILEN];
(void)strcpy(mypath, prog_arg);
if ((sp = vms_pathleaf(mypath)) == mypath)
(void)strcpy(mypath, current_directory(FALSE));
else
*sp = EOS;
if (!tb_init(&myfiles, EOS)
|| !tb_sappend(&myfiles, mypath)
|| !tb_sappend(&myfiles, ",SYS$SYSTEM:,SYS$LIBRARY:")
|| !tb_append(&myfiles, EOS))
return NULL;
}
path = tb_values(myfiles);
#else /* UNIX or MSDOS */
path = getenv("PATH"); /* get the PATH variable */
#endif
while ((path = parse_pathlist(path, fspec)) != 0) {
if (ffaccess(pathcat(fspec, fspec, fname), mode)) {
return(fspec);
}
}
#endif /* OPT_PATHLOOKUP */
#endif /* ENVFUNC */
}
return NULL; /* no such luck */
}
/* translate a keycode to its binding-string */
char *
kcod2pstr(
int c, /* sequence to translate */
char *seq) /* destination string for sequence */
{
seq[0] = (char)kcod2escape_seq(c, &seq[1]);
return seq;
}
/* Translate a 16-bit keycode to a string that will replay into the same
* code.
*/
int
kcod2escape_seq (
int c,
char * ptr)
{
char *base = ptr;
/* ...just for completeness */
if (c & CTLA) *ptr++ = (char)cntl_a;
else if (c & CTLX) *ptr++ = (char)cntl_x;
else if (c & SPEC) *ptr++ = (char)poundc;
*ptr++ = (char)c;
*ptr = EOS;
return (int)(ptr - base);
}
/* translates a binding string into printable form */
#if OPT_REBIND
static char *
bytes2prc(char *dst, char *src, int n)
{
char *base = dst;
register int c;
register const char *tmp;
for ( ; n != 0; dst++, src++, n--) {
c = *src;
tmp = 0;
if (c & HIGHBIT) {
*dst++ = 'M';
*dst++ = '-';
c &= ~HIGHBIT;
}
if (c == ' ') {
tmp = "<space>";
} else if (iscntrl(c)) {
*dst++ = '^';
*dst = tocntrl(c);
} else {
*dst = (char)c;
}
if (tmp != 0) {
while ((*dst++ = *tmp++) != EOS)
;
dst -= 2; /* point back to last nonnull */
}
if (n > 1) {
*++dst = '-';
}
}
*dst = EOS;
return base;
}
/* translate a 10-bit keycode to its printable name (like "M-j") */
static char *
kcod2prc(
int c, /* sequence to translate */
char *seq) /* destination string for sequence */
{
char temp[NSTRING];
(void)kcod2pstr(c,temp);
return bytes2prc(seq, temp + 1, *temp);
}
#endif
/* kcode2kbind: translate a 10-bit key-binding to the table-pointer
*/
static KBIND *
kcode2kbind(register int code)
{
register KBIND *kbp; /* pointer into a binding table */
#if OPT_REBIND
for (kbp = KeyBindings; kbp != kbindtbl; kbp = kbp->k_link) {
if (kbp->k_code == code)
return kbp;
}
#endif
for (kbp = kbindtbl; kbp->k_cmd; kbp++) {
if (kbp->k_code == code)
return kbp;
}
return 0;
}
/* kcod2fnc: translate a 10-bit keycode to a function pointer */
/* (look a key binding up in the binding table) */
const CMDFUNC *
kcod2fnc(
int c) /* key to find what is bound to it */
{
if (isspecial(c)) {
register KBIND *kp = kcode2kbind(c);
return (kp != 0) ? kp->k_cmd : 0;
}
return asciitbl[c];
}
#if !SMALLER
/* fnc2kcod: translate a function pointer to a keycode */
int
fnc2kcod(const CMDFUNC *f)
{
register KBIND *kbp;
register int c;
for (c = 0; c < N_chars; c++)
if (f == asciitbl[c])
return c;
#if OPT_REBIND
for (kbp = KeyBindings; kbp != kbindtbl; kbp = kbp->k_link) {
if (kbp->k_cmd == f)
return kbp->k_code;
}
#endif
for (kbp = kbindtbl; kbp->k_cmd != 0; kbp++) {
if (kbp->k_cmd == f)
return kbp->k_code;
}
return -1; /* none found */
}
#endif
/* fnc2pstr: translate a function pointer to a pascal-string that a user
could enter. returns a pointer to a static array */
#if DISP_X11
char *
fnc2pstr(const CMDFUNC *f)
{
register int c;
static char seq[10];
c = fnc2kcod(f);
if (c == -1)
return NULL;
return kcod2pstr(c, seq);
}
#endif
/* fnc2engl: translate a function pointer to the english name for
that function
*/
#if OPT_EVAL || OPT_REBIND
static const NTAB *
fnc2ntab(const CMDFUNC *cfp)
{
register const NTAB *nptr; /* pointer into the name table */
register const NTAB *shortnptr = NULL; /* pointer into the name table */
/* skim through the table, looking for a match */
for (nptr = nametbl; nptr->n_cmd; nptr++) {
if (nptr->n_cmd == cfp) {
/* if it's a long name, return it */
if ((int)strlen(nptr->n_name) > SHORT_CMD_LEN)
return nptr;
/* remember the first short name, in case there's
no long name */
if (!shortnptr)
shortnptr = nptr;
}
}
if (shortnptr)
return shortnptr;
return NULL;
}
static char *
fnc2engl(const CMDFUNC *cfp)
{
register const NTAB *nptr = fnc2ntab(cfp);
return nptr ? nptr->n_name : NULL;
}
#endif
/* engl2fnc: match name to a function in the names table
translate english name to function pointer
return any match or NULL if none
*/
#define BINARY_SEARCH_IS_BROKEN 0
#if BINARY_SEARCH_IS_BROKEN /* then use the old linear look-up */
const CMDFUNC *
engl2fnc(const char *fname) /* name to attempt to match */
{
register NTAB *nptr; /* pointer to entry in name binding table */
register SIZE_T len = strlen(fname);
if (len != 0) { /* scan through the table, returning any match */
nptr = nametbl;
while (nptr->n_cmd != NULL) {
if (strncmp(fname, nptr->n_name, len) == 0)
return nptr->n_cmd;
++nptr;
}
}
return NULL;
}
#else
/* this runs 10 times faster for 'nametbl[]' */
const CMDFUNC *
engl2fnc(const char *fname) /* name to attempt to match */
{
int lo, hi, cur;
int r;
register SIZE_T len = strlen(fname);
if (len == 0)
return NULL;
/* scan through the table, returning any match */
lo = 0;
hi = nametblsize - 2; /* don't want last entry -- it's NULL */
while (lo <= hi) {
cur = (lo + hi) >> 1;
if ((r = strncmp(fname, nametbl[cur].n_name, len)) == 0) {
/* Now find earliest matching entry */
while (cur > lo
&& strncmp(fname, nametbl[cur-1].n_name, len) == 0)
cur--;
return nametbl[cur].n_cmd;
} else if (r > 0) {
lo = cur+1;
} else {
hi = cur-1;
}
}
return NULL;
}
#endif /* binary vs linear */
/* prc2kcod: translate printable code to 10 bit keycode */
#if OPT_EVAL || OPT_REBIND
static int
prc2kcod(
const char *kk) /* name of key to translate to Command key form */
{
register UINT c; /* key sequence to return */
register UINT pref = 0; /* key prefixes */
register int len = strlen(kk);
register const UCHAR *k = (const UCHAR *)kk;
if (len > 3 && *(k+2) == '-') {
if (*k == '^') {
if (iscntrl(cntl_a) && *(k+1) == toalpha(cntl_a))
pref = CTLA;
if (iscntrl(cntl_x) && *(k+1) == toalpha(cntl_x))
pref = CTLX;
if (iscntrl(poundc) && *(k+1) == toalpha(poundc))
pref = SPEC;
} else if (!strncmp((const char *)k, "FN", (SIZE_T)2)) {
pref = SPEC;
}
if (pref != 0)
k += 3;
} else if (len > 2 && !strncmp((const char *)k, "M-", (SIZE_T)2)) {
pref = HIGHBIT;
k += 2;
} else if (len > 1) {
if (*k == cntl_a)
pref = CTLA;
else if (*k == cntl_x)
pref = CTLX;
else if (*k == poundc)
pref = SPEC;
if (pref != 0) {
k++;
if (len > 2 && *k == '-')
k++;
}
}
/* a control char? */
if (*k == '^' && *(k+1) != EOS) {
c = *(k+1);
if (islower(c)) c = toupper(c);
c = tocntrl(c);
k += 2;
} else { /* any single char, control or not */
c = *k++;
}
if (*k != EOS) /* we should have eaten the whole thing */
return -1;
return (int)(pref|c);
}
#endif
#if OPT_EVAL
/* translate printable code (like "M-r") to english command name */
const char *
prc2engl( /* string key name to binding name.... */
const char *skey) /* name of key to get binding for */
{
const char *bindname;
int c;
c = prc2kcod(skey);
if (c < 0)
return "ERROR";
bindname = fnc2engl(kcod2fnc(c));
if (bindname == NULL)
bindname = "ERROR";
return bindname;
}
#endif
/*
* Get an english command name from the user
*/
char *
kbd_engl(
const char *prompt, /* null pointer to splice calls */
char *buffer)
{
if (kbd_engl_stat(prompt, buffer) == TRUE)
return buffer;
return NULL;
}
/* sound the alarm! */
void
kbd_alarm(void)
{
if (global_g_val(GMDERRORBELLS)) {
TTbeep();
TTflush();
}
warnings++;
}
/* put a character to the keyboard-prompt, updating 'ttcol' */
void
kbd_putc(int c)
{
beginDisplay;
if ((kbd_expand <= 0) && isreturn(c)) {
TTputc(c);
ttcol = 0;
} else if (isprint(c)) {
if (ttcol < term.t_ncol-1) /* -1 to avoid auto-wrap problems */
TTputc(c);
ttcol++;
} else if ((kbd_expand < 0) && (c == '\t')) {
kbd_putc(' ');
} else {
if (c & HIGHBIT) {
kbd_putc('\\');
if (global_w_val(WMDNONPRINTOCTAL)) {
kbd_putc(((c>>6)&3)+'0');
kbd_putc(((c>>3)&7)+'0');
kbd_putc(((c )&7)+'0');
} else {
kbd_putc('x');
kbd_putc(hexdigits[(c>>4) & 0xf]);
kbd_putc(hexdigits[(c ) & 0xf]);
}
} else {
kbd_putc('^');
kbd_putc(toalpha(c));
}
}
endofDisplay;
}
/* put a string to the keyboard-prompt */
void
kbd_puts(const char *s)
{
while (*s)
kbd_putc(*s++);
}
/* erase a character from the display by wiping it out */
void
kbd_erase(void)
{
beginDisplay;
if (ttcol > 0) {
if (--ttcol < term.t_ncol-1) {
TTputc('\b');
TTputc(' ');
TTputc('\b');
}
} else
ttcol = 0;
endofDisplay;
}
#if OPT_CASELESS
static int
cs_strcmp(
int case_insensitive,
const char *s1,
const char *s2)
{
if (case_insensitive)
return stricmp(s1, s2);
return strcmp(s1, s2);
}
static int
cs_strncmp(
int case_insensitive,
const char *s1,
const char *s2,
SIZE_T n)
{
if (case_insensitive)
return strnicmp(s1, s2, n);
return strncmp(s1, s2, n);
}
#endif /* OPT_CASELESS */
/* definitions for name-completion */
#define NEXT_DATA(p) ((p)+size_entry)
#define PREV_DATA(p) ((p)-size_entry)
#ifdef lint
static /*ARGSUSED*/
const char * THIS_NAME(const char *p) { return 0; }
#else
#define THIS_NAME(p) (*(const char *const *)(p))
#endif
#define NEXT_NAME(p) THIS_NAME(NEXT_DATA(p))
/*
* Scan down until we no longer match the current input, or reach the end of
* the symbol table.
*/
/*ARGSUSED*/
static const char *
skip_partial(
int case_insensitive,
char *buf,
SIZE_T len,
const char *table,
SIZE_T size_entry)
{
register const char * next = NEXT_DATA(table);
register const char * sp;
while ((sp = THIS_NAME(next)) != 0) {
if (StrNcmp(buf, sp, len) != 0)
break;
next = NEXT_DATA(next);
}
return next;
}
/*
* Shows a partial-match. This is invoked in the symbol table at a partial
* match, and the user wants to know what characters could be typed next.
* If there is more than one possibility, they are shown in square-brackets.
* If there is only one possibility, it is shown in curly-braces.
*/
static void
show_partial(
int case_insensitive,
char *buf,
SIZE_T len,
const char *table,
SIZE_T size_entry)
{
register const char *next = skip_partial(case_insensitive, buf, len, table, size_entry);
register const char *last = PREV_DATA(next);
register int c;
if (THIS_NAME(table)[len] == THIS_NAME(last)[len]) {
kbd_putc('{');
while ((c = THIS_NAME(table)[len]) != 0) {
if (c == THIS_NAME(last)[len]) {
kbd_putc(c);
len++;
} else
break;
}
kbd_putc('}');
}
if (next != NEXT_DATA(table)) {
c = TESTC; /* shouldn't be in the table! */
kbd_putc('[');
while (table != next) {
register const char *sp = THIS_NAME(table);
if (c != sp[len]) {
c = sp[len];
kbd_putc(c ? c : '$');
}
table = NEXT_DATA(table);
}
kbd_putc(']');
}
TTflush();
}
#if OPT_POPUPCHOICE
/*
* makecmpllist is called from liststuff to display the possible completions.
*/
struct compl_rec {
char *buf;
SIZE_T len;
const char *table;
SIZE_T size_entry;
};
#ifdef lint
#define c2ComplRec(c) ((struct compl_rec *)0)
#else
#define c2ComplRec(c) ((struct compl_rec *)c)
#endif
/*ARGSUSED*/
static void
makecmpllist(
int case_insensitive,
void *cinfop)
{
char * buf = c2ComplRec(cinfop)->buf;
SIZE_T len = c2ComplRec(cinfop)->len;
const char * first = c2ComplRec(cinfop)->table;
SIZE_T size_entry = c2ComplRec(cinfop)->size_entry;
register const char *last = skip_partial(case_insensitive, buf, len, first, size_entry);
register const char *p;
SIZE_T maxlen;
int slashcol;
int cmpllen;
int cmplcols;
int cmplrows;
int nentries;
int i, j;
for (p = NEXT_DATA(first), maxlen = strlen(THIS_NAME(first));
p != last;
p = NEXT_DATA(p)) {
SIZE_T l = strlen(THIS_NAME(p));
if (l > maxlen)
maxlen = l;
}
slashcol = (int)(pathleaf(buf) - buf);
if (slashcol != 0) {
char b[NLINE];
(void)strncpy(b, buf, (SIZE_T)slashcol);
(void)strncpy(&b[slashcol], &(THIS_NAME(first))[slashcol],
(len-slashcol));
b[slashcol+(len-slashcol)] = EOS;
bprintf("Completions prefixed by %s:\n", b);
}
cmplcols = term.t_ncol / (maxlen - slashcol + 1);
if (cmplcols == 0)
cmplcols = 1;
nentries = (int)(last - first) / size_entry;
cmplrows = nentries / cmplcols;
cmpllen = term.t_ncol / cmplcols;
if (cmplrows * cmplcols < nentries)
cmplrows++;
for (i = 0; i < cmplrows; i++) {
for (j = 0; j < cmplcols; j++) {
int idx = cmplrows * j + i;
if (idx < nentries) {
const char *s = THIS_NAME(first+(idx*size_entry))+slashcol;
if (j == cmplcols-1)
bprintf("%s\n", s);
else
bprintf("%*s", cmpllen, s);
}
else {
bprintf("\n");
break;
}
}
}
}
/*
* Pop up a window and show the possible completions.
*/
static void
show_completions(
int case_insensitive,
char *buf,
SIZE_T len,
const char *table,
SIZE_T size_entry)
{
struct compl_rec cinfo;
BUFFER *bp;
int alreadypopped = 0;
/*
* Find out if completions buffer exists; so we can take the time to
* shrink/grow the window to the latest size.
*/
if ((bp = find_b_name(COMPLETIONS_BufName)) != NULL) {
alreadypopped = (bp->b_nwnd != 0);
}
cinfo.buf = buf;
cinfo.len = len;
cinfo.table = table;
cinfo.size_entry = size_entry;
liststuff(COMPLETIONS_BufName, FALSE, makecmpllist, case_insensitive, (void *) &cinfo);
if (alreadypopped)
shrinkwrap();
(void)update(TRUE);
}
/*
* Scroll the completions window wrapping around back to the beginning
* of the buffer once it has been completely scrolled. If the completions
* buffer is missing for some reason, we will call show_completions to pop
* it (back) up.
*/
static void
scroll_completions(
int case_insensitive,
char *buf,
SIZE_T len,
const char *table,
SIZE_T size_entry)
{
BUFFER *bp = find_b_name(COMPLETIONS_BufName);
if (bp == NULL)
show_completions(case_insensitive, buf, len, table, size_entry);
else {
LINEPTR lp;
swbuffer(bp);
(void)gotoeos(FALSE, 1);
lp = DOT.l;
(void)forwhpage(FALSE, 1);
if (lp == DOT.l)
(void)gotobob(FALSE, 0);
(void)update(TRUE);
}
}
void
popdown_completions(void)
{
BUFFER *bp;
if ((bp = find_b_name(COMPLETIONS_BufName)) != NULL)
zotwp(bp);
}
#endif /* OPT_POPUPCHOICE */
/*
* Attempt to partial-complete the string, char at a time
*/
static int
fill_partial(
int case_insensitive,
char *buf,
SIZE_T pos,
const char *first,
const char *last,
SIZE_T size_entry)
{
register const char *p;
register int n = pos;
const char *this_name = THIS_NAME(first);
#if 0 /* case insensitive reply correction doesn't work reliably yet */
if (!clexec && case_insensitive) {
int spos = pos;
while (spos > 0 && buf[spos - 1] != SLASHC) {
kbd_erase();
spos--;
}
while (spos < pos) {
kbd_putc(this_name[spos]);
spos++;
}
}
#endif
for_ever {
buf[n] = this_name[n]; /* add the next char in */
buf[n+1] = EOS;
/* scan through the candidates */
for (p = NEXT_DATA(first); p != last; p = NEXT_DATA(p)) {
if (StrNcmp(&THIS_NAME(p)[n], &buf[n], 1) != 0) {
buf[n] = EOS;
if (n == pos
#if OPT_POPUPCHOICE
# if OPT_ENUM_MODES
&& !global_g_val(GVAL_POPUP_CHOICES)
# else
&& !global_g_val(GMDPOPUP_CHOICES)
# endif
#endif
)
kbd_alarm();
TTflush(); /* force out alarm or partial completion */
return n;
}
}
if (!clexec)
kbd_putc(buf[n]); /* add the character */
n++;
}
}
static int testcol; /* records the column when TESTC is decoded */
#if OPT_POPUPCHOICE
/*
* cmplcol is used to record the column number (on the message line) after
* name completion. Its value is used to decide whether or not to display
* a completion list if the name completion character (tab) is pressed
* twice in succession. Once the completion list has been displayed, its
* value will be changed to the additive inverse of the column number in
* order to determine whether to scroll if tab is pressed yet again. We
* assume that 0 will never be a valid column number. So long as we always
* display some sort of prompt prior to reading from the message line, this
* is a good assumption.
*/
static int cmplcol = 0;
#endif
/*
* Initializes the name-completion logic
*/
void
kbd_init(void)
{
testcol = -1;
}
/*
* Erases the display that was shown in response to TESTC
*/
void
kbd_unquery(void)
{
beginDisplay;
#if OPT_POPUPCHOICE
if (cmplcol != ttcol && -cmplcol != ttcol)
cmplcol = 0;
#endif
if (testcol >= 0) {
while (ttcol > testcol)
kbd_erase();
TTflush();
testcol = -1;
}
endofDisplay;
}
/*
* This is invoked to find the closest name to complete from the current buffer
* contents.
*/
int
kbd_complete(
int case_insensitive,
int c, /* TESTC, NAMEC or isreturn() */
char *buf,
int *pos,
const char *table,
SIZE_T size_entry)
{
register SIZE_T cpos = *pos;
register const char *nbp; /* first ptr to entry in name binding table */
int status = FALSE;
#if OPT_POPUPCHOICE
# if OPT_ENUM_MODES
int gvalpopup_choices = global_g_val(GVAL_POPUP_CHOICES);
# else
int gvalpopup_choices = global_g_val(GMDPOPUP_CHOICES);
# endif
#endif
kbd_init(); /* nothing to erase */
buf[cpos] = EOS; /* terminate it for us */
nbp = table; /* scan for matches */
while (THIS_NAME(nbp) != NULL) {
if (StrNcmp(buf, THIS_NAME(nbp), strlen(buf)) == 0) {
testcol = ttcol;
/* a possible match! exact? no more than one? */
#if OPT_POPUPCHOICE
if (!clexec && c == NAMEC && cmplcol == -ttcol) {
scroll_completions(case_insensitive, buf, cpos, nbp, size_entry);
return FALSE;
}
#endif
if (c == TESTC) {
show_partial(case_insensitive, buf, cpos, nbp, size_entry);
}
else if (Strcmp(buf, THIS_NAME(nbp)) == 0 || /* exact? */
NEXT_NAME(nbp) == NULL ||
StrNcmp(buf, NEXT_NAME(nbp), strlen(buf)) != 0)
{
/* exact or only one like it. print it */
if (!clexec) {
#if 0 /* case insensitive reply correction doesn't work reliably yet */
if (case_insensitive) {
int spos = cpos;
while (spos > 0 && buf[spos - 1] != SLASHC) {
kbd_erase();
spos--;
}
kbd_puts(THIS_NAME(nbp) + spos);
}
else
#endif
kbd_puts(THIS_NAME(nbp) + cpos);
TTflush();
testcol = ttcol;
}
if (c != NAMEC) /* put it back */
unkeystroke(c);
/* return complete name */
(void)strncpy0(buf, THIS_NAME(nbp),
(SIZE_T)(NLINE - 1));
*pos = strlen(buf);
#if OPT_POPUPCHOICE
if (gvalpopup_choices != POPUP_CHOICES_OFF
&& !clexec && (c == NAMEC))
status = FALSE;
else
#endif
status = TRUE;
}
else {
/* try for a partial match against the list */
*pos = fill_partial(case_insensitive, buf, cpos, nbp,
skip_partial(case_insensitive, buf, cpos, nbp, size_entry),
size_entry);
testcol = ttcol;
}
#if OPT_POPUPCHOICE
# if OPT_ENUM_MODES
if (!clexec
&& gvalpopup_choices != POPUP_CHOICES_OFF
&& c == NAMEC
&& *pos == cpos) {
if (gvalpopup_choices == POPUP_CHOICES_IMMED
|| cmplcol == ttcol) {
show_completions(case_insensitive, buf, cpos, nbp, size_entry);
cmplcol = -ttcol;
}
else
cmplcol = ttcol;
}
else
cmplcol = 0;
# else
if (!clexec && gvalpopup_choices
&& c == NAMEC && *pos == cpos) {
show_completions(case_insensitive, buf, cpos, nbp, size_entry);
cmplcol = -ttcol;
}
else
cmplcol = 0;
# endif
#endif
return status;
}
nbp = NEXT_DATA(nbp);
}
#if OPT_POPUPCHOICE
cmplcol = 0;
#endif
kbd_alarm(); /* no match */
buf[*pos = cpos] = EOS;
return FALSE;
}
/*
* Test a buffer to see if it looks like a shift-command, which may have
* repeated characters (but they must all be the same).
*/
static int
is_shift_cmd(
char *buffer,
int cpos)
{
register int c = *buffer;
if (isRepeatable(c)) {
while (--cpos > 0)
if (*(++buffer) != c)
return FALSE;
return TRUE;
}
return FALSE;
}
/*
* The following mess causes the command to terminate if:
*
* we've got the eolchar
* -or-
* we're in the first few chars and we're switching from punctuation
* (i.e., delimiters) to non-punctuation (i.e., characters that are part
* of command-names), or vice-versa. oh yeah -- '-' isn't punctuation
* today, and '!' isn't either, in one direction, at any rate.
* All this allows things like:
* : e#
* : e!%
* : !ls
* : q!
* : up-line
* to work properly.
*
* If we pass this "if" with c != NAMEC, then c is ungotten below,
* so it can be picked up by the commands argument getter later.
*/
#define ismostpunct(c) (ispunct(c) && (c) != '-')
static int
eol_command(
char * buffer,
int cpos,
int c,
int eolchar)
{
/*
* Handle special case of repeated-character implying repeat-count
*/
if (is_shift_cmd(buffer, cpos) && (c == *buffer))
return TRUE;
/*
* Shell-commands aren't complete until the line is complete.
*/
if ((cpos > 0) && isShellOrPipe(buffer))
return isreturn(c);
return (c == eolchar)
|| (
cpos > 0 && cpos < 3
&&(
(!ismostpunct(c)
&& ismostpunct(buffer[cpos-1])
)
|| ((c != '!' && ismostpunct(c))
&& (buffer[cpos-1] == '!' || !ismostpunct(buffer[cpos-1]))
)
)
);
}
/*
* This procedure is invoked from 'kbd_string()' to setup the command-name
* completion and query displays.
*/
static int
cmd_complete(
int c,
char *buf,
int *pos)
{
register int status;
#if OPT_HISTORY
/*
* If the user scrolled back in 'edithistory()', the text may be a
* repeated-shift command, which won't match the command-table (e.g.,
* ">>>").
*/
if ((*pos > 1) && is_shift_cmd(buf, *pos)) {
int len = 1;
char tmp[NLINE];
tmp[0] = *buf;
tmp[1] = EOS;
status = cmd_complete(c, tmp, &len);
} else
#endif
if ((*pos > 0) && isShellOrPipe(buf)) {
#if COMPLETE_FILES
status = shell_complete(c, buf, pos);
#else
status = isreturn(c);
if (c != NAMEC)
unkeystroke(c);
#endif
} else {
status = kbd_complete(FALSE, c, buf, pos,
(const char *)&nametbl[0], sizeof(nametbl[0]));
}
return status;
}
int
kbd_engl_stat(
const char *prompt,
char *buffer)
{
int kbd_flags = KBD_EXPCMD|KBD_NULLOK|((NAMEC != ' ') ? 0 : KBD_MAYBEC);
*buffer = EOS;
#if COMPLETE_FILES
init_filec(FILECOMPLETION_BufName);
#endif
return kbd_reply(
prompt, /* no-prompt => splice */
buffer, /* in/out buffer */
NLINE, /* sizeof(buffer) */
eol_command,
' ', /* eolchar */
kbd_flags, /* allow blank-return */
cmd_complete);
}
#if NO_LEAKS
void
bind_leaks(void)
{
#if OPT_REBIND
while (KeyBindings != kbindtbl) {
KBIND *kbp = KeyBindings;
KeyBindings = kbp->k_link;
free((char *)kbp);
}
#endif
}
#endif /* NO_LEAKS */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.