2.11BSD/src/bin/tcsh/tc.bind.c
/* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/tc.bind.c,v 3.0 1991/07/04 21:49:28 christos 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 "config.h"
#if !defined(lint) && !defined(pdp11)
static char *rcsid()
{ return "$Id: tc.bind.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; }
#endif
#include "sh.h"
#include "ed.h"
#include "ed.defns.h"
static int str7cmp __P((char *, char *));
static int tocontrol __P((int));
static char *u_p_key __P((int));
static KEYCMD getkeycmd __P((Char **));
static int parsekey __P((Char **));
static void prntky __P((KEYCMD *, Char *));
static KEYCMD parsecmd __P((Char *));
static Char *parsestring __P((Char *, Char *));
static void print_all_keys __P((void));
static void prntkys __P((KEYCMD *, int, int));
static void bindkey_usage __P((void));
static void list_functions __P((void));
static void pkeys __P((int, int));
extern int MapsAreInited;
/* like strcmp, but compairisons are striped to 7 bits
(due to shell stupidness) */
static int
str7cmp(a, b)
register char *a, *b;
{
while ((*a & TRIM) == (*b++ & TRIM))
if (!*a++)
return (0);
b--;
return ((*a & TRIM) - (*b & TRIM));
}
static int
tocontrol(c)
int c;
{
c &= CHAR;
if (Islower(c))
c = Toupper(c);
else if (c == ' ')
c = '@';
if (c == '?')
c = 0177;
else
c &= 037;
return (c);
}
static char *
u_p_key(c) /* 'c' -> "c", '^C' -> "^" + "C" */
register int c;
{
register char *cp;
static char tmp[10];
cp = tmp;
if (c & 0400) {
*cp++ = 'A';
*cp++ = '-';
c &= 0377;
}
if ((c & META) && !(Isprint(c) || Iscntrl(c) && Isprint(c | 0100))) {
*cp++ = 'M';
*cp++ = '-';
c &= ASCII;
}
if (Isprint(c)) {
*cp++ = c;
*cp = '\0';
return (tmp);
}
else if (c == ' ') {
(void) strcpy(cp, "Spc");
return (tmp);
}
else if (c == '\n') {
(void) strcpy(cp, "Lfd");
return (tmp);
}
else if (c == '\r') {
(void) strcpy(cp, "Ret");
return (tmp);
}
else if (c == '\t') {
(void) strcpy(cp, "Tab");
return (tmp);
}
else if (c == '\033') {
(void) strcpy(cp, "Esc");
return (tmp);
}
else if (c == '\177') {
(void) strcpy(cp, "Del");
return (tmp);
}
else {
*cp++ = '^';
if (c == '\177') {
*cp++ = '?';
}
else {
*cp++ = c | 0100;
}
*cp = '\0';
return (tmp);
}
}
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 u_p_d character
* for return value -2 (xkeynext) */
{
register int c, meta = 0, control = 0, ctrlx = 0;
Char *s = *sp;
KEYCMD keycmd;
if (s == NULL) {
xprintf("bad key specification -- null string\n");
return -1;
}
if (*s == 0) {
xprintf("bad key specification -- empty string\n");
return -1;
}
(void) strip(s); /* trim to 7 bits. */
if (s[1] == 0) /* single char */
return (s[0] & 0377);
if ((s[0] == 'F' || s[0] == 'f') && s[1] == '-') {
if (s[2] == 0) {
xprintf("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("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("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("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("Bad function-key specification.\n");
xprintf("Null key not allowed\n");
return (-1);
}
*sp = s;
return (-2);
}
if (s[1] != 0) { /* if symbolic name */
char *ts;
ts = short2str(s);
if (!str7cmp(ts, "space") || !str7cmp(ts, "Spc"))
c = ' ';
else if (!str7cmp(ts, "return") || !str7cmp(ts, "Ret"))
c = '\r';
else if (!str7cmp(ts, "newline") || !str7cmp(ts, "Lfd"))
c = '\n';
else if (!str7cmp(ts, "linefeed"))
c = '\n';
else if (!str7cmp(ts, "tab"))
c = '\t';
else if (!str7cmp(ts, "escape") || !str7cmp(ts, "Esc"))
c = '\033';
else if (!str7cmp(ts, "backspace"))
c = '\b';
else if (!str7cmp(ts, "delete"))
c = '\177';
else {
xprintf("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);
}
void
dobindkey(v)
Char **v;
{
KEYCMD *map;
int string, no, remove;
Char *par;
Char p;
Char inbuf[200];
Char outbuf[200];
Char *in;
Char *out;
KEYCMD cmd;
if (!MapsAreInited)
ed_IMaps();
map = CcKeyMap;
string = 0;
remove = 0;
for (no = 1, par = v[no];
par != NULL && (*par++ & CHAR) == '-'; no++, par = v[no]) {
if ((p = (*par & CHAR)) == '-') {
break;
}
else if (p == 'a') {
map = CcAltMap;
}
else if (p == 's') {
string = 1;
}
else if (p == 'r') {
remove = 1;
}
else if (p == 'v') {
ed_IVIMaps();
return;
}
else if (p == 'e') {
ed_IEmacsMaps();
return;
}
else if (p == 'd') {
#ifdef VIDEFAULT
ed_IVIMaps();
#else
ed_IEmacsMaps();
#endif
return;
}
else if (p == 'l') {
list_functions();
return;
}
else {
bindkey_usage();
return;
}
}
if (!v[no]) {
print_all_keys();
return;
}
if ((in = parsestring(v[no++], inbuf)) == NULL)
return;
if (remove) {
if (in[1]) {
(void) DeleteXkey(in);
}
else if (map[(unsigned char) *in] == F_XKEY) {
(void) DeleteXkey(in);
map[(unsigned char) *in] = F_UNASSIGNED;
}
else {
map[(unsigned char) *in] = F_UNASSIGNED;
}
return;
}
if (!v[no]) {
prntky(map, in);
return;
}
if (v[no + 1]) {
bindkey_usage();
return;
}
if (string) {
if ((out = parsestring(v[no], outbuf)) == NULL)
return;
AddXkey(in, out);
map[(unsigned char) *in] = F_XKEY;
}
else {
if ((cmd = parsecmd(v[no])) == 0)
return;
if (in[1]) {
AddXKeyCmd(in, (Char) cmd);
map[(unsigned char) *in] = F_XKEY;
}
else {
(void) ClearXkey(map, in);
map[(unsigned char) *in] = cmd;
}
}
}
static void
prntky(map, in)
KEYCMD *map;
Char *in;
{
unsigned char outbuf[100];
register struct KeyFuncs *fp;
if (in[0] == 0 || in[1] == 0) {
(void) u_p_string(in, outbuf);
for (fp = FuncNames; fp->name; fp++) {
if (fp->func == map[(unsigned char) *in]) {
xprintf("%s\t->\t%s\n", outbuf, fp->name);
}
}
}
else {
(void) PrintXkey(in);
}
}
static KEYCMD
parsecmd(str)
Char *str;
{
register struct KeyFuncs *fp;
for (fp = FuncNames; fp->name; fp++) {
if (str7cmp(short2str(str), fp->name) == 0) {
return fp->func;
}
}
xprintf("Bad command name: %s\n", short2str(str));
return 0;
}
int
parseescape(ptr)
Char **ptr;
{
Char *p, c;
p = *ptr;
if ((p[1] & CHAR) == 0) {
xprintf("Something must follow: %c\\n", *p);
return 0;
}
if ((*p & CHAR) == '\\') {
p++;
switch (*p & CHAR) {
case 'a':
c = '\007'; /* Bell */
break;
case 'b':
c = '\010'; /* Backspace */
break;
case 't':
c = '\011'; /* Horizontal Tab */
break;
case 'n':
c = '\012'; /* New Line */
break;
case 'v':
c = '\013'; /* Vertical Tab */
break;
case 'f':
c = '\014'; /* Form Feed */
break;
case 'r':
c = '\015'; /* Carriage Return */
break;
case 'e':
c = '\033'; /* Escape */
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
{
register int cnt, val, ch;
for (cnt = 0, val = 0; cnt < 3; cnt++) {
ch = *p++ & CHAR;
if (ch < '0' || ch > '7') {
p--;
break;
}
val = (val << 3) | (ch - '0');
}
if ((val & 0xffffff00) != 0) {
xprintf("Octal constant does not fit in a char.\n");
return 0;
}
c = val;
--p;
}
break;
default:
c = *p;
break;
}
}
else if ((*p & CHAR) == '^') {
p++;
c = (*p == '?') ? '\177' : ((*p & CHAR) & 0237);
}
else
c = *p;
*ptr = p;
return (c);
}
static Char *
parsestring(str, buf)
Char *str;
Char *buf;
{
Char *b;
Char *p;
b = buf;
if (*str == 0) {
xprintf("Null string specification\n");
return 0;
}
for (p = str; *p != 0; p++) {
if ((*p & CHAR) == '\\' || (*p & CHAR) == '^') {
if ((*b++ = parseescape(&p)) == 0)
return 0;
}
else {
*b++ = *p & CHAR;
}
}
*b = 0;
return buf;
}
unsigned char *
u_p_string(str, buf)
Char *str;
unsigned char *buf;
{
unsigned char *b;
Char *p;
b = buf;
*b++ = '"';
if (*str == 0) {
*b++ = '^';
*b++ = '@';
*b++ = '"';
*b++ = 0;
return buf;
}
for (p = str; *p != 0; p++) {
if (Iscntrl(*p)) {
*b++ = '^';
if (*p == '\177')
*b++ = '?';
else
*b++ = *p | 0100;
}
else if (*p == '^' || *p == '\\') {
*b++ = '\\';
*b++ = *p;
}
else if (*p == ' ' || (Isprint(*p) && !Isspace(*p))) {
*b++ = *p;
}
else {
*b++ = '\\';
*b++ = ((*p >> 6) & 7) + '0';
*b++ = ((*p >> 3) & 7) + '0';
*b++ = (*p & 7) + '0';
}
}
*b++ = '"';
*b++ = 0;
return buf; /* should check for overflow */
}
static void
print_all_keys()
{
int prev, i;
xprintf("Standard key bindings\n");
prev = 0;
for (i = 0; i < 256; i++) {
if (CcKeyMap[prev] == CcKeyMap[i])
continue;
prntkys(CcKeyMap, prev, i - 1);
prev = i;
}
prntkys(CcKeyMap, prev, i - 1);
xprintf("Alternative key bindings\n");
prev = 0;
for (i = 0; i < 256; i++) {
if (CcAltMap[prev] == CcAltMap[i])
continue;
prntkys(CcAltMap, prev, i - 1);
prev = i;
}
prntkys(CcAltMap, prev, i - 1);
xprintf("Multi-character bindings\n");
(void) PrintXkey(STRNULL); /* print all Xkey bindings */
}
static void
prntkys(map, first, last)
KEYCMD *map;
int first, last;
{
register struct KeyFuncs *fp;
Char firstbuf[2], lastbuf[2];
unsigned char unparsbuf[10], extrabuf[10];
firstbuf[0] = first;
firstbuf[1] = 0;
lastbuf[0] = last;
lastbuf[1] = 0;
if (map[first] == F_UNASSIGNED) {
if (first == last)
xprintf("%-15s-> is undefined\n",
u_p_string(firstbuf, unparsbuf));
return;
}
for (fp = FuncNames; fp->name; fp++) {
if (fp->func == map[first]) {
if (first == last) {
xprintf("%-15s-> %s\n",
u_p_string(firstbuf, unparsbuf), fp->name);
}
else {
xprintf("%-4s to %-7s-> %s\n",
u_p_string(firstbuf, unparsbuf),
u_p_string(lastbuf, extrabuf), fp->name);
}
return;
}
}
if (map == CcKeyMap) {
xprintf("BUG!!! %s isn't bound to anything.\n",
u_p_string(firstbuf, unparsbuf));
xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
}
else {
xprintf("BUG!!! %s isn't bound to anything.\n",
u_p_string(firstbuf, unparsbuf));
xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
}
}
static void
bindkey_usage()
{
xprintf(
"Usage: bindkey [options] [--] [in-string [out-string | command]]\n");
xprintf(" -a bind key in alternative key binding\n");
xprintf(" -s bind an out-string instad of a command\n");
xprintf(" -v initialized maps to default vi bindings\n");
xprintf(" -e initialized maps to default emacs bindings\n");
xprintf(" -d initialized maps to default bindings\n");
#ifdef LONGFUNCS
xprintf(" -l list available functions with descriptions\n");
#else
xprintf(" -l list available functions\n");
#endif /* LONGFUNCS */
xprintf(" -r remove the binding of in-string\n");
xprintf(
"\nIn no out-string or command is given, the binding for in-string\n");
xprintf("is printed or all bindings if in-strings is not given.\n");
}
static void
list_functions()
{
register struct KeyFuncs *fp;
for (fp = FuncNames; fp->name; fp++) {
#ifdef LONGFUNCS
xprintf("%s\n %s\n", fp->name, fp->description);
#else
xprintf("%s\n", fp->name);
#endif
}
}
void
dobind(v)
register Char **v;
{
register int c;
register struct KeyFuncs *fp;
register int i, prev;
Char *p, *l;
Char buf[1000];
/*
* 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_IMaps();
if (v[1] && v[2] && v[3]) {
xprintf(
"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 (str7cmp(short2str(v[1]), fp->name) == 0) {
Char *s = v[2];
if ((c = parsekey(&s)) == -1)
return;
if (c == -2) { /* extented key */
for (i = 0; i < 256; i++) {
if (i != 033 && (CcKeyMap[i] == F_XKEY ||
CcAltMap[i] == F_XKEY)) {
p = buf;
if (i > 0177) {
*p++ = 033;
*p++ = i & ASCII;
}
else {
*p++ = i;
}
for (l = s; *l != 0; l++) {
*p++ = *l;
}
*p = 0;
AddXKeyCmd(buf, fp->func);
}
}
return;
}
if (c & 0400) {
if (VImode) {
CcAltMap[c & 0377] = fp->func;
/* bind the vi cmd mode key */
if (c & META) {
buf[0] = 033;
buf[1] = c & ASCII;
buf[2] = 0;
AddXKeyCmd(buf, fp->func);
}
}
else {
buf[0] = 030; /* ^X */
buf[1] = c & 0377;
buf[2] = 0;
AddXKeyCmd(buf, fp->func);
CcKeyMap[030] = F_XKEY;
}
}
else {
CcKeyMap[c] = fp->func; /* bind the key */
if (c & META) {
buf[0] = 033;
buf[1] = c & ASCII;
buf[2] = 0;
AddXKeyCmd(buf, fp->func);
}
}
return;
}
}
stderror(ERR_NAME | ERR_STRING, "Invalid function");
}
else if (v[1]) {
char *cv = short2str(v[1]);
if (str7cmp(cv, "list") == 0) {
for (fp = FuncNames; fp->name; fp++) {
xprintf("%s\n", fp->name);
}
return;
}
if ((str7cmp(cv, "emacs") == 0) ||
#ifndef VIDEFAULT
(str7cmp(cv, "defaults") == 0) ||
(str7cmp(cv, "default") == 0) ||
#endif
(str7cmp(cv, "mg") == 0) ||
(str7cmp(cv, "gnumacs") == 0)) {
/* reset keys to default */
ed_IEmacsMaps();
#ifdef VIDEFAULT
}
else if ((str7cmp(cv, "vi") == 0)
|| (str7cmp(cv, "default") == 0)
|| (str7cmp(cv, "defaults") == 0)) {
#else
}
else if (str7cmp(cv, "vi") == 0) {
#endif
ed_IVIMaps();
}
else { /* want to know what this key does */
Char *s = v[1];
if ((c = parsekey(&s)) == -1)
return;
if (c == -2) { /* extended key */
(void) PrintXkey(s);
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 & 0377] == CcAltMap[i & 0377])
continue;
pkeys(prev, i - 1);
prev = i;
}
pkeys(prev, i - 1);
(void) PrintXkey(STRNULL); /* print all Xkey bindings */
}
return;
}
static void
pkeys(first, last)
register int first, last;
{
register struct KeyFuncs *fp;
register KEYCMD *map;
char buf[8];
if (last & 0400) {
map = CcAltMap;
first &= 0377;
last &= 0377;
}
else {
map = CcKeyMap;
}
if (map[first] == F_UNASSIGNED) {
if (first == last)
#ifdef _SEQUENT_
xprintf(" %s\t\tis undefined\n",
u_p_key(map == CcAltMap ? first | 0400 : first));
#else /* _SEQUENT_ */
xprintf(" %s\t\tis undefined\n", u_p_key(first));
#endif /* _SEQUENT_ */
return;
}
for (fp = FuncNames; fp->name; fp++) {
if (fp->func == map[first]) {
if (first == last) {
xprintf(" %s\t\t%s\n",
u_p_key((first & 0377) | (map == CcAltMap ? 0400 : 0)),
fp->name);
}
else {
(void) strcpy(buf, u_p_key((first & 0377) |
(map == CcAltMap ? 0400 : 0)));
xprintf(" %s..%s\t\t%s\n", buf,
u_p_key((last & 0377) | (map == CcAltMap ? 0400 : 0)),
fp->name);
}
return;
}
}
if (map == CcKeyMap) {
xprintf("BUG!!! %s isn't bound to anything.\n", u_p_key(first));
xprintf("CcKeyMap[%d] == %d\n", first, CcKeyMap[first]);
}
else {
xprintf("BUG!!! %s isn't bound to anything.\n",
u_p_key(first & 0400));
xprintf("CcAltMap[%d] == %d\n", first, CcAltMap[first]);
}
}