tc.bind.c   [plain text]


/* $Header: /cvs/Darwin/src/live/tcsh/tcsh/tc.bind.c,v 1.1.1.2 2001/06/28 23:10:53 bbraun Exp $ */
/*
 * tc.bind.c: Key binding functions
 */
/*-
 * Copyright (c) 1980, 1991 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "sh.h"

RCSID("$Id: tc.bind.c,v 1.1.1.2 2001/06/28 23:10:53 bbraun Exp $")

#include "ed.h"
#include "ed.defns.h"

#ifdef OBSOLETE
static	int    tocontrol	__P((int));
static	char  *unparsekey	__P((int));
static	KEYCMD getkeycmd	__P((Char **));
static	int    parsekey		__P((Char **));
static	void   pkeys		__P((int, int));
#endif /* OBSOLETE */

static	void   printkey		__P((KEYCMD *, CStr *));
static	KEYCMD parsecmd		__P((Char *));
static  void   bad_spec		__P((Char *));
static	CStr  *parsestring	__P((Char *, CStr *));
static	CStr  *parsebind	__P((Char *, CStr *));
static	void   print_all_keys	__P((void));
static	void   printkeys	__P((KEYCMD *, int, int));
static	void   bindkey_usage	__P((void));
static	void   list_functions	__P((void));

extern int MapsAreInited;




/*ARGSUSED*/
void
dobindkey(v, c)
    Char  **v;
    struct command *c;
{
    KEYCMD *map;
    int     ntype, no, remove, key, bind;
    Char   *par;
    Char    p;
    KEYCMD  cmd;
    CStr    in;
    CStr    out;
    Char    inbuf[200];
    Char    outbuf[200];
    uChar   ch;
    in.buf = inbuf;
    out.buf = outbuf;
    in.len = 0;
    out.len = 0;

    USE(c);
    if (!MapsAreInited)
	ed_InitMaps();

    map = CcKeyMap;
    ntype = XK_CMD;
    key = remove = bind = 0;
    for (no = 1, par = v[no]; 
	 par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
	if ((p = (*par & CHAR)) == '-') {
	    no++;
	    break;
	}
	else 
	    switch (p) {
	    case 'b':
		bind = 1;
		break;
	    case 'k':
		key = 1;
		break;
	    case 'a':
		map = CcAltMap;
		break;
	    case 's':
		ntype = XK_STR;
		break;
	    case 'c':
		ntype = XK_EXE;
		break;
	    case 'r':
		remove = 1;
		break;
	    case 'v':
		ed_InitVIMaps();
		return;
	    case 'e':
		ed_InitEmacsMaps();
		return;
	    case 'd':
#ifdef VIDEFAULT
		ed_InitVIMaps();
#else /* EMACSDEFAULT */
		ed_InitEmacsMaps();
#endif /* VIDEFAULT */
		return;
	    case 'l':
		list_functions();
		return;
	    default:
		bindkey_usage();
		return;
	    }
    }

    if (!v[no]) {
	print_all_keys();
	return;
    }

    if (key) {
	if (!IsArrowKey(v[no]))
	    xprintf(CGETS(20, 1, "Invalid key name `%S'\n"), v[no]);
	in.buf = v[no++];
	in.len = Strlen(in.buf);
    }
    else {
	if (bind) {
	    if (parsebind(v[no++], &in) == NULL)
		return;
	}
	else {
	    if (parsestring(v[no++], &in) == NULL)
		return;
	}
    }

    ch = (uChar) in.buf[0];

    if (remove) {
	if (key) {
	    (void) ClearArrowKeys(&in);
	    return;
	}
	if (in.len > 1) {
	    (void) DeleteXkey(&in);
	}
	else if (map[ch] == F_XKEY) {
	    (void) DeleteXkey(&in);
	    map[ch] = F_UNASSIGNED;
	}
	else {
	    map[ch] = F_UNASSIGNED;
	}
	return;
    }
    if (!v[no]) {
	if (key)
	    PrintArrowKeys(&in);
	else
	    printkey(map, &in);
	return;
    }
    if (v[no + 1]) {
	bindkey_usage();
	return;
    }
    switch (ntype) {
    case XK_STR:
    case XK_EXE:
	if (parsestring(v[no], &out) == NULL)
	    return;
	if (key) {
	    if (SetArrowKeys(&in, XmapStr(&out), ntype) == -1)
		xprintf(CGETS(20, 2, "Bad key name: %S\n"), in);
	}
	else
	    AddXkey(&in, XmapStr(&out), ntype);
	map[ch] = F_XKEY;
	break;
    case XK_CMD:
	if ((cmd = parsecmd(v[no])) == 0)
	    return;
	if (key)
	    (void) SetArrowKeys(&in, XmapCmd((int) cmd), ntype);
	else {
	    if (in.len > 1) {
		AddXkey(&in, XmapCmd((int) cmd), ntype);
		map[ch] = F_XKEY;
	    }
	    else {
		ClearXkey(map, &in);
		map[ch] = cmd;
	    }
	}
	break;
    default:
	abort();
	break;
    }
    if (key)
	BindArrowKeys();
}

static void
printkey(map, in)
    KEYCMD *map;
    CStr   *in;
{
    unsigned char outbuf[100];
    register struct KeyFuncs *fp;

    if (in->len < 2) {
	(void) unparsestring(in, outbuf, STRQQ);
	for (fp = FuncNames; fp->name; fp++) {
	    if (fp->func == map[(uChar) *(in->buf)]) {
		xprintf("%s\t->\t%s\n", outbuf, fp->name);
	    }
	}
    }
    else 
	PrintXkey(in);
}

static  KEYCMD
parsecmd(str)
    Char   *str;
{
    register struct KeyFuncs *fp;

    for (fp = FuncNames; fp->name; fp++) {
	if (strcmp(short2str(str), fp->name) == 0) {
	    return (KEYCMD) fp->func;
	}
    }
    xprintf(CGETS(20, 3, "Bad command name: %S\n"), str);
    return 0;
}


static void
bad_spec(str)
    Char *str;
{
    xprintf(CGETS(20, 4, "Bad key spec %S\n"), str);
}

static CStr *
parsebind(s, str)
    Char *s;
    CStr *str;
{
#ifdef DSPMBYTE
    extern bool NoNLSRebind;
#endif /* DSPMBYTE */
    Char *b = str->buf;

    if (Iscntrl(*s)) {
	*b++ = *s;
	*b = '\0';
	str->len = (int) (b - str->buf);
	return str;
    }

    switch (*s) {
    case '^':
	s++;
#ifdef IS_ASCII
	*b++ = (*s == '?') ? '\177' : ((*s & CHAR) & 0237);
#else
	*b++ = (*s == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*s & CHAR] & 0237];
#endif
	*b = '\0';
	break;

    case 'F':
    case 'M':
    case 'X':
    case 'C':
#ifdef WINNT_NATIVE
    case 'N':
#endif /* WINNT_NATIVE */
	if (s[1] != '-' || s[2] == '\0') {
	    bad_spec(s);
	    return NULL;
	}
	s += 2;
	switch (s[-2]) {
	case 'F': case 'f':	/* Turn into ^[str */
	    *b++ = CTL_ESC('\033');
	    while ((*b++ = *s++) != '\0')
		continue;
	    b--;
	    break;

	case 'C': case 'c':	/* Turn into ^c */
#ifdef IS_ASCII
	    *b++ = (*s == '?') ? '\177' : ((*s & CHAR) & 0237);
#else
	    *b++ = (*s == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*s & CHAR] & 0237];
#endif
	    *b = '\0';
	    break;

	case 'X' : case 'x':	/* Turn into ^Xc */
#ifdef IS_ASCII
	    *b++ = 'X' & 0237;
#else
	    *b++ = _toebcdic[_toascii['X'] & 0237];
#endif
	    *b++ = *s;
	    *b = '\0';
	    break;

	case 'M' : case 'm':	/* Turn into 0x80|c */
#ifdef DSPMBYTE
	    if (!NoNLSRebind) {
	    	*b++ = CTL_ESC('\033');
	    	*b++ = *s;
	    } else {
#endif /* DSPMBYTE */
#ifdef IS_ASCII
	    *b++ = *s | 0x80;
#else
	    *b++ = _toebcdic[_toascii[*s] | 0x80];
#endif
#ifdef DSPMBYTE
	    }
#endif /* DSPMBYTE */
	    *b = '\0';
	    break;
#ifdef WINNT_NATIVE
	case 'N' : case 'n':	/* NT */
		{
			Char bnt;

			bnt = nt_translate_bindkey(s);
			if (bnt != 0)
				*b++ = bnt;
			else
				bad_spec(s);
		}
	    break;
#endif /* WINNT_NATIVE */

	default:
	    abort();
	    /*NOTREACHED*/
	    return NULL;
	}
	break;

    default:
	bad_spec(s);
	return NULL;
    }

    str->len = (int) (b - str->buf);
    return str;
}


static CStr *
parsestring(str, buf)
    Char   *str;
    CStr   *buf;
{
    Char   *b;
    const Char   *p;
    int    es;

    b = buf->buf;
    if (*str == 0) {
	xprintf(CGETS(20, 5, "Null string specification\n"));
	return NULL;
    }

    for (p = str; *p != 0; p++) {
	if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
	    if ((es = parseescape(&p)) == -1)
		return 0;
	    else
		*b++ = (Char) es;
	}
	else
	    *b++ = *p & CHAR;
    }
    *b = 0;
    buf->len = (int) (b - buf->buf);
    return buf;
}

static void
print_all_keys()
{
    int     prev, i;
    CStr nilstr;
    nilstr.buf = NULL;
    nilstr.len = 0;


    xprintf(CGETS(20, 6, "Standard key bindings\n"));
    prev = 0;
    for (i = 0; i < 256; i++) {
	if (CcKeyMap[prev] == CcKeyMap[i])
	    continue;
	printkeys(CcKeyMap, prev, i - 1);
	prev = i;
    }
    printkeys(CcKeyMap, prev, i - 1);

    xprintf(CGETS(20, 7, "Alternative key bindings\n"));
    prev = 0;
    for (i = 0; i < 256; i++) {
	if (CcAltMap[prev] == CcAltMap[i])
	    continue;
	printkeys(CcAltMap, prev, i - 1);
	prev = i;
    }
    printkeys(CcAltMap, prev, i - 1);
    xprintf(CGETS(20, 8, "Multi-character bindings\n"));
    PrintXkey(NULL);	/* print all Xkey bindings */
    xprintf(CGETS(20, 9, "Arrow key bindings\n"));
    PrintArrowKeys(&nilstr);
}

static void
printkeys(map, first, last)
    KEYCMD *map;
    int     first, last;
{
    register struct KeyFuncs *fp;
    Char    firstbuf[2], lastbuf[2];
    CStr fb, lb;
    unsigned char unparsbuf[10], extrabuf[10];
    fb.buf = firstbuf;
    lb.buf = lastbuf;

    firstbuf[0] = (Char) first;
    firstbuf[1] = 0;
    lastbuf[0] = (Char) last;
    lastbuf[1] = 0;
    fb.len = 1;
    lb.len = 1;

    if (map[first] == F_UNASSIGNED) {
	if (first == last)
	    xprintf(CGETS(20, 10, "%-15s->  is undefined\n"),
		    unparsestring(&fb, unparsbuf, STRQQ));
	return;
    }

    for (fp = FuncNames; fp->name; fp++) {
	if (fp->func == map[first]) {
	    if (first == last) {
		xprintf("%-15s->  %s\n",
			unparsestring(&fb, unparsbuf, STRQQ), fp->name);
	    }
	    else {
		xprintf("%-4s to %-7s->  %s\n",
			unparsestring(&fb, unparsbuf, STRQQ),
			unparsestring(&lb, extrabuf, STRQQ), fp->name);
	    }
	    return;
	}
    }
    if (map == CcKeyMap) {
	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
		unparsestring(&fb, unparsbuf, STRQQ));
	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
    }
    else {
	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
		unparsestring(&fb, unparsbuf, STRQQ));
	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
    }
}

static void
bindkey_usage()
{
    xprintf(CGETS(20, 12,
	    "Usage: bindkey [options] [--] [KEY [COMMAND]]\n"));
    xprintf(CGETS(20, 13,
    	    "    -a   list or bind KEY in alternative key map\n"));
    xprintf(CGETS(20, 14,
	    "    -b   interpret KEY as a C-, M-, F- or X- key name\n"));
    xprintf(CGETS(20, 15,
            "    -s   interpret COMMAND as a literal string to be output\n"));
    xprintf(CGETS(20, 16,
            "    -c   interpret COMMAND as a builtin or external command\n"));
    xprintf(CGETS(20, 17,
	    "    -v   bind all keys to vi bindings\n"));
    xprintf(CGETS(20, 18,
	    "    -e   bind all keys to emacs bindings\n"));
    xprintf(CGETS(20, 19,
	    "    -d   bind all keys to default editor's bindings\n"));
    xprintf(CGETS(20, 20,
	    "    -l   list editor commands with descriptions\n"));
    xprintf(CGETS(20, 21,
	    "    -r   remove KEY's binding\n"));
    xprintf(CGETS(20, 22,
	    "    -k   interpret KEY as a symbolic arrow-key name\n"));
    xprintf(CGETS(20, 23,
	    "    --   force a break from option processing\n"));
    xprintf(CGETS(20, 24,
	    "    -u   (or any invalid option) this message\n"));
    xprintf("\n");
    xprintf(CGETS(20, 25,
	    "Without KEY or COMMAND, prints all bindings\n"));
    xprintf(CGETS(20, 26,
	    "Without COMMAND, prints the binding for KEY.\n"));
}

static void
list_functions()
{
    register struct KeyFuncs *fp;

    for (fp = FuncNames; fp->name; fp++) {
	xprintf("%s\n          %s\n", fp->name, fp->desc);
    }
}

#ifdef OBSOLETE

/*
 * Unfortunately the apollo optimizer does not like & operations
 * with 0377, and produces illegal instructions. So we make it
 * an unsigned char, and hope for the best.
 * Of-course the compiler is smart enough to produce bad assembly
 * language instructions, but dumb when it comes to fold the constant :-)
 */
#ifdef apollo
static unsigned char APOLLO_0377 = 0377;
#else /* sane */
# define APOLLO_0377    0377
#endif /* apollo */

static int
tocontrol(c)
    int    c;
{
    c &= CHAR;
    if (Islower(c))
	c = Toupper(c);
    else if (c == ' ')
	c = '@';
    if (c == '?')
	c = CTL_ESC('\177');
    else
#ifdef IS_ASCII
	c &= 037;
#else
	/* EBCDIC: simulate ASCII-behavior by transforming to ASCII and back */
	c  = _toebcdic[_toascii[c] & 037];
#endif
    return (c);
}

static char *
unparsekey(c)			/* 'c' -> "c", '^C' -> "^" + "C" */
    register int c;
{
    register char *cp;
    static char tmp[10];

    cp = tmp;

    if (c & 0400) {
	*cp++ = 'A';
	*cp++ = '-';
	c &= APOLLO_0377;
    }
    if ((c & META) && !(Isprint(c) || (Iscntrl(c) && Isprint(c | 0100)))) {
	*cp++ = 'M';
	*cp++ = '-';
	c &= ASCII;
    }
    if (Isprint(c)) {
	*cp++ = (char) c;
	*cp = '\0';
	return (tmp);
    }
    switch (c) {
    case ' ':
	(void) strcpy(cp, "Spc");
	return (tmp);
    case '\n':
	(void) strcpy(cp, "Lfd");
	return (tmp);
    case '\r':
	(void) strcpy(cp, "Ret");
	return (tmp);
    case '\t':
	(void) strcpy(cp, "Tab");
	return (tmp);
#ifdef IS_ASCII
    case '\033':
	(void) strcpy(cp, "Esc");
	return (tmp);
    case '\177':
	(void) strcpy(cp, "Del");
	return (tmp);
    default:
	*cp++ = '^';
	if (c == '\177') {
	    *cp++ = '?';
	}
	else {
	    *cp++ = c | 0100;
	}
	*cp = '\0';
	return (tmp);
#else /* IS_ASCII */
    default:
        if (*cp == CTL_ESC('\033')) {
	    (void) strcpy(cp, "Esc");
	    return (tmp);
	}
	else if (*cp == CTL_ESC('\177')) {
	    (void) strcpy(cp, "Del");
	    return (tmp);
	}
	else if (Isupper(_toebcdic[_toascii[c]|0100])
		|| strchr("@[\\]^_", _toebcdic[_toascii[c]|0100]) != NULL) {
	    *cp++ = '^';
	    *cp++ = _toebcdic[_toascii[c]|0100]
	}
	else {
	    xsnprintf(cp, 3, "\\%3.3o", c);
	    cp += 4;
	}
#endif /* IS_ASCII */
    }
}

static  KEYCMD
getkeycmd(sp)
    Char  **sp;
{
    register Char *s = *sp;
    register char c;
    register KEYCMD keycmd = F_UNASSIGNED;
    KEYCMD *map;
    int     meta = 0;
    Char   *ret_sp = s;

    map = CcKeyMap;

    while (*s) {
	if (*s == '^' && s[1]) {
	    s++;
	    c = tocontrol(*s++);
	}
	else
	    c = *s++;

	if (*s == '\0')
	    break;

	switch (map[c | meta]) {
	case F_METANEXT:
	    meta = META;
	    keycmd = F_METANEXT;
	    ret_sp = s;
	    break;

	case F_XKEY:
	    keycmd = F_XKEY;
	    ret_sp = s;
	    /* FALLTHROUGH */

	default:
	    *sp = ret_sp;
	    return (keycmd);

	}
    }
    *sp = ret_sp;
    return (keycmd);
}

static int
parsekey(sp)
    Char  **sp;			/* Return position of first unparsed character
				 * for return value -2 (xkeynext) */
{
    register int c, meta = 0, control = 0, ctrlx = 0;
    Char   *s = *sp;
    KEYCMD  keycmd;

    if (s == NULL) {
	xprintf(CGETS(20, 27, "bad key specification -- null string\n"));
	return -1;
    }
    if (*s == 0) {
	xprintf(CGETS(20, 28, "bad key specification -- empty string\n"));
	return -1;
    }

    (void) strip(s);		/* trim to 7 bits. */

    if (s[1] == 0)		/* single char */
	return (s[0] & APOLLO_0377);

    if ((s[0] == 'F' || s[0] == 'f') && s[1] == '-') {
	if (s[2] == 0) {
	    xprintf(CGETS(20, 29,
		   "Bad function-key specification.  Null key not allowed\n"));
	    return (-1);
	}
	*sp = s + 2;
	return (-2);
    }

    if (s[0] == '0' && s[1] == 'x') {	/* if 0xn, then assume number */
	c = 0;
	for (s += 2; *s; s++) {	/* convert to hex; skip the first 0 */
	    c *= 16;
	    if (!Isxdigit(*s)) {
		xprintf(CGETS(20, 30,
			"bad key specification -- malformed hex number\n"));
		return -1;	/* error */
	    }
	    if (Isdigit(*s))
		c += *s - '0';
	    else if (*s >= 'a' && *s <= 'f')
		c += *s - 'a' + 0xA;
	    else if (*s >= 'F' && *s <= 'F')
		c += *s - 'A' + 0xA;
	}
    }
    else if (s[0] == '0' && Isdigit(s[1])) {	/* if 0n, then assume number */
	c = 0;
	for (s++; *s; s++) {	/* convert to octal; skip the first 0 */
	    if (!Isdigit(*s) || *s == '8' || *s == '9') {
		xprintf(CGETS(20, 31,
			"bad key specification -- malformed octal number\n"));
		return -1;	/* error */
	    }
	    c = (c * 8) + *s - '0';
	}
    }
    else if (Isdigit(s[0]) && Isdigit(s[1])) {	/* decimal number */
	c = 0;
	for (; *s; s++) {	/* convert to octal; skip the first 0 */
	    if (!Isdigit(*s)) {
		xprintf(CGETS(20, 32,
		       "bad key specification -- malformed decimal number\n"));
		return -1;	/* error */
	    }
	    c = (c * 10) + *s - '0';
	}
    }
    else {
	keycmd = getkeycmd(&s);

	if ((s[0] == 'X' || s[0] == 'x') && s[1] == '-') {	/* X- */
	    ctrlx++;
	    s += 2;
	    keycmd = getkeycmd(&s);
	}
	if ((*s == 'm' || *s == 'M') && s[1] == '-') {	/* meta */
	    meta++;
	    s += 2;
	    keycmd = getkeycmd(&s);
	}
	else if (keycmd == F_METANEXT && *s) {	/* meta */
	    meta++;
	    keycmd = getkeycmd(&s);
	}
	if (*s == '^' && s[1]) {
	    control++;
	    s++;
	    keycmd = getkeycmd(&s);
	}
	else if ((*s == 'c' || *s == 'C') && s[1] == '-') {	/* control */
	    control++;
	    s += 2;
	    keycmd = getkeycmd(&s);
	}

	if (keycmd == F_XKEY) {
	    if (*s == 0) {
		xprintf(CGETS(20, 33,
			      "Bad function-key specification.\n"));
		xprintf(CGETS(20, 34, "Null key not allowed\n"));
		return (-1);
	    }
	    *sp = s;
	    return (-2);
	}

	if (s[1] != 0) {	/* if symbolic name */
	    char   *ts;

	    ts = short2str(s);
	    if (!strcmp(ts, "space") || !strcmp(ts, "Spc"))
		c = ' ';
	    else if (!strcmp(ts, "return") || !strcmp(ts, "Ret"))
		c = '\r';
	    else if (!strcmp(ts, "newline") || !strcmp(ts, "Lfd"))
		c = '\n';
	    else if (!strcmp(ts, "linefeed"))
		c = '\n';
	    else if (!strcmp(ts, "tab"))
		c = '\t';
	    else if (!strcmp(ts, "escape") || !strcmp(ts, "Esc"))
		c = CTL_ESC('\033');
	    else if (!strcmp(ts, "backspace"))
		c = '\b';
	    else if (!strcmp(ts, "delete"))
		c = CTL_ESC('\177');
	    else {
		xprintf(CGETS(20, 35,
			"bad key specification -- unknown name \"%S\"\n"), s);
		return -1;	/* error */
	    }
	}
	else
	    c = *s;		/* just a single char */

	if (control)
	    c = tocontrol(c);
	if (meta)
	    c |= META;
	if (ctrlx)
	    c |= 0400;
    }
    return (c & 0777);
}


/*ARGSUSED*/
void
dobind(v, dummy)
    register Char **v;
    struct command *dummy;
{
    register int c;
    register struct KeyFuncs *fp;
    register int i, prev;
    Char   *p, *l;
    CStr    cstr;
    Char    buf[1000];

    USE(dummy);
    /*
     * Assume at this point that i'm given 2 or 3 args - 'bind', the f-name,
     * and the key; or 'bind' key to print the func for that key.
     */

    if (!MapsAreInited)
	ed_InitMaps();

    if (v[1] && v[2] && v[3]) {
	xprintf(CGETS(20, 36,
	 "usage: bind [KEY | COMMAND KEY | \"emacs\" | \"vi\" | \"-a\"]\n"));
	return;
    }

    if (v[1] && v[2]) {		/* if bind FUNCTION KEY */
	for (fp = FuncNames; fp->name; fp++) {
	    if (strcmp(short2str(v[1]), fp->name) == 0) {
		Char   *s = v[2];

		if ((c = parsekey(&s)) == -1)
		    return;
		if (c == -2) {	/* extended key */
		    for (i = 0; i < 256; i++) {
			if (i != CTL_ESC('\033') && (CcKeyMap[i] == F_XKEY ||
					 CcAltMap[i] == F_XKEY)) {
			    p = buf;
#ifdef IS_ASCII
			    if (i > 0177) {
				*p++ = 033;
				*p++ = i & ASCII;
			    }
			    else {
				*p++ = (Char) i;
			    }
#else
			    *p++ = (Char) i;
#endif
			    for (l = s; *l != 0; l++) {
				*p++ = *l;
			    }
			    *p = 0;
			    cstr.buf = buf;
			    cstr.len = Strlen(buf);
			    AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
			}
		    }
		    return;
		}
		if (c & 0400) {
		    if (VImode) {
			CcAltMap[c & APOLLO_0377] = fp->func;	
			/* bind the vi cmd mode key */
			if (c & META) {
			    buf[0] = CTL_ESC('\033');
			    buf[1] = c & ASCII;
			    buf[2] = 0;
			    cstr.buf = buf;
			    cstr.len = Strlen(buf);
			    AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
			}
		    }
		    else {
			buf[0] = CTL_ESC('\030');	/* ^X */
			buf[1] = c & APOLLO_0377;
			buf[2] = 0;
			cstr.buf = buf;
			cstr.len = Strlen(buf);
			AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
			CcKeyMap[CTL_ESC('\030')] = F_XKEY;
		    }
		}
		else {
		    CcKeyMap[c] = fp->func;	/* bind the key */
		    if (c & META) {
			buf[0] = CTL_ESC('\033');
			buf[1] = c & ASCII;
			buf[2] = 0;
			cstr.buf = buf;
			cstr.len = Strlen(buf);
			AddXkey(&cstr, XmapCmd(fp->func), XK_CMD);
		    }
		}
		return;
	    }
	}
	stderror(ERR_NAME | ERR_STRING, CGETS(20, 37, "Invalid function"));
    }
    else if (v[1]) {
	char   *cv = short2str(v[1]);

	if (strcmp(cv, "list") == 0) {
	    for (fp = FuncNames; fp->name; fp++) {
		xprintf("%s\n", fp->name);
	    }
	    return;
	}
	if ((strcmp(cv, "emacs") == 0) ||
#ifndef VIDEFAULT
	    (strcmp(cv, "defaults") == 0) ||
	    (strcmp(cv, "default") == 0) ||
#endif
	    (strcmp(cv, "mg") == 0) ||
	    (strcmp(cv, "gnumacs") == 0)) {
	    /* reset keys to default */
	    ed_InitEmacsMaps();
#ifdef VIDEFAULT
	}
	else if ((strcmp(cv, "vi") == 0)
		 || (strcmp(cv, "default") == 0)
		 || (strcmp(cv, "defaults") == 0)) {
#else
	}
	else if (strcmp(cv, "vi") == 0) {
#endif
	    ed_InitVIMaps();
	}
	else {			/* want to know what this key does */
	    Char   *s = v[1];

	    if ((c = parsekey(&s)) == -1)
		return;
	    if (c == -2) {	/* extended key */
		cstr.buf = s;
		cstr.len = Strlen(s);
		PrintXkey(&cstr);
		return;
	    }
	    pkeys(c, c);	/* must be regular key */
	}
    }
    else {			/* list all the bindings */
	prev = 0;
	for (i = 0; i < 256; i++) {
	    if (CcKeyMap[prev] == CcKeyMap[i])
		continue;
	    pkeys(prev, i - 1);
	    prev = i;
	}
	pkeys(prev, i - 1);
	prev = 0;
	for (i = 256; i < 512; i++) {
	    if (CcAltMap[prev & APOLLO_0377] == CcAltMap[i & APOLLO_0377])
		continue;
	    pkeys(prev, i - 1);
	    prev = i;
	}
	pkeys(prev, i - 1);
	cstr.buf = NULL;
	cstr.len = 0;
	PrintXkey(&cstr);	/* print all Xkey bindings */
    }
    return;
}

static void
pkeys(first, last)
    register int first, last;
{
    register struct KeyFuncs *fp;
    register KEYCMD *map;
    int mask;
    char    buf[8];

    if (last & 0400) {
	map = CcAltMap;
	first &= APOLLO_0377;
	last &= APOLLO_0377;
	mask = 0400;
    }
    else {
	map = CcKeyMap;
	mask = 0;
    }
    if (map[first] == F_UNASSIGNED) {
	if (first == last)
	    xprintf(CGETS(20, 38, " %s\t\tis undefined\n"),
		    unparsekey(first | mask));
	return;
    }

    for (fp = FuncNames; fp->name; fp++) {
	if (fp->func == map[first]) {
	    if (first == last) 
		xprintf(" %s\t\t%s\n", 
			unparsekey((first & APOLLO_0377) | mask), fp->name);
	    else {
		(void) strcpy(buf, unparsekey((first & APOLLO_0377) | mask));
		xprintf(" %s..%s\t\t%s\n", buf,
		        unparsekey((last & APOLLO_0377) | mask), fp->name);
	    }
	    return;
	}
    }
    if (map == CcKeyMap) {
	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
		unparsekey(first));
	xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
    }
    else {
	xprintf(CGETS(20, 11, "BUG!!! %s isn't bound to anything.\n"),
		unparsekey(first & 0400));
	xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
    }
}
#endif /* OBSOLETE */