2.11BSD/src/bin/tcsh/ed.xmap.c
/* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/ed.xmap.c,v 3.0 1991/07/04 21:49:28 christos Exp $ */
/*
* ed.xmap.c: This module contains the procedures for maintaining
* the extended-key map.
*
* An extended-key (Xkey) is a sequence of keystrokes
* introduced with an sequence introducer and consisting
* of an arbitrary number of characters. This module maintains
* a map (the Xmap) to convert these extended-key sequences
* into input strings or editor functions. It contains the
* following externally visible functions.
*
* int GetXkey(ch,code);
* Char *ch;
* Char **code;
*
* Looks up *ch in map and then reads characters until a
* complete match is found or a mismatch occurs. Returns 1
* for command and 0 for a string. Returns NULL in code for
* no match and 0 as value. The last character read is returned
* in *ch.
*
* void AddXkey(Xkey, code);
* Char *Xkey;
* Char * code;
*
* void AddXKeyCmd(Xkey, CmdCode);
* Char *Xkey;
* Char CmdCode;
*
* Adds Xkey to the Xmap and associates the code with it. If
* Xkey is already is in Xmap, the new code is applied to the
* existing Xkey.
*
* int DeleteXkey(Xkey);
* Char *Xkey;
*
* Delete the Xkey and all longer Xkeys staring with Xkey, if
* they exists.
*
* Warning:
* If Xkey is a substring of some other Xkeys, then the longer
* Xkeys are lost!! That is, if the Xkeys "abcd" and "abcef"
* are in Xmap, adding the key "abc" will cause the first two
* definitions to be lost.
*
* void ResetXmap();
*
* Removes all entries from Xmap and resets the defaults.
*
* void PrintXkey(Xkey);
* Char *Xkey;
*
* Prints all extended keys prefixed by Xkey and their associated
* commands.
*
* Restrictions:
* -------------
* 1) It is not possible to have one Xkey that is a
* substring of another.
*/
/*-
* 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: ed.xmap.c,v 3.0 1991/07/04 21:49:28 christos Exp $"; }
#endif
#include "sh.h"
#include "ed.h"
#include "ed.defns.h"
#ifndef NULL
#define NULL 0
#endif
/* Internal Data types and declarations */
/* The Nodes of the Xmap. The Xmap is a linked list of these node
* elements
*/
typedef struct Xmapnode {
Char ch; /* single character of Xkey */
Char *code; /* command code or pointer to string, if this
* is a leaf */
struct Xmapnode *next; /* ptr to next char of this Xkey */
struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */
} XmapNode;
static XmapNode *Xmap = NULL; /* the current Xmap */
static Char CurCode;
static XmapNode CurCmd = {0, &CurCode, NULL, NULL};
/* Some declarations of procedures */
static int TraverseMap __P((XmapNode *, Char *, Char **));
static int TryNode __P((XmapNode *, Char *, Char *, int));
static XmapNode *GetFreeNode __P((int));
static void PutFreeNode __P((XmapNode *));
static int TryDeleteNode __P((XmapNode **, Char *));
/* ResetXmap():
* Takes all nodes on Xmap and puts them on free list. Then
* initializes Xmap with arrow keys
*/
void
ResetXmap(vi)
int vi;
{
static Char strA[] = {033, '[', 'A', '\0'};
static Char strB[] = {033, '[', 'B', '\0'};
static Char strC[] = {033, '[', 'C', '\0'};
static Char strD[] = {033, '[', 'D', '\0'};
static Char stOA[] = {033, 'O', 'A', '\0'};
static Char stOB[] = {033, 'O', 'B', '\0'};
static Char stOC[] = {033, 'O', 'C', '\0'};
static Char stOD[] = {033, 'O', 'D', '\0'};
PutFreeNode(Xmap);
Xmap = NULL;
AddXKeyCmd(strA, F_UP_HIST);
AddXKeyCmd(strB, F_DOWN_HIST);
AddXKeyCmd(strC, F_CHARFWD);
AddXKeyCmd(strD, F_CHARBACK);
AddXKeyCmd(stOA, F_UP_HIST);
AddXKeyCmd(stOB, F_DOWN_HIST);
AddXKeyCmd(stOC, F_CHARFWD);
AddXKeyCmd(stOD, F_CHARBACK);
if (vi) {
AddXKeyCmd(&strA[1], F_UP_HIST);
AddXKeyCmd(&strB[1], F_DOWN_HIST);
AddXKeyCmd(&strC[1], F_CHARFWD);
AddXKeyCmd(&strD[1], F_CHARBACK);
AddXKeyCmd(&stOA[1], F_UP_HIST);
AddXKeyCmd(&stOB[1], F_DOWN_HIST);
AddXKeyCmd(&stOC[1], F_CHARFWD);
AddXKeyCmd(&stOD[1], F_CHARBACK);
}
return;
}
/* GetXkey():
* Calls the recursive function with entry point Xmap
*/
int
GetXkey(ch, code)
Char *ch;
Char **code;
{
return (TraverseMap(Xmap, ch, code));
}
/* TraverseMap():
* recursively traverses node in tree until match or mismatch is
* found. May read in more characters.
*/
static int
TraverseMap(ptr, ch, code)
XmapNode *ptr;
Char *ch;
Char **code;
{
Char tch;
if (ptr->ch == *ch) {
/* match found */
if (ptr->next) {
if (ptr->next != &CurCmd) {
/* Xkey not complete so get next char */
if (G_N_Char(&tch) != 1) { /* if EOF or error */
*ch = 0;
CurCmd.code[0] = F_SEND_EOF;
*code = CurCmd.code;
return 1; /* PWP: Pretend we just read an end-of-file */
}
*ch = tch;
return (TraverseMap(ptr->next, ch, code));
}
else {
CurCmd.code[0] = (Char) ptr->code;
*code = CurCmd.code;
return 1;
}
}
else {
/* next is null so this is leaf node and a string */
*ch = 0;
*code = ptr->code;
return 0;
}
}
else {
/* no match found here */
if (ptr->sibling) {
/* try next sibling */
return (TraverseMap(ptr->sibling, ch, code));
}
else {
/* no next sibling -- mismatch */
*code = NULL;
return 0;
}
}
}
void
AddXkey(Xkey, code)
Char *Xkey;
Char *code;
{
if (Xkey[0] == '\0') {
xprintf("AddXkey: Null extended-key not allowed.\n");
return;
}
if (Xmap == NULL)
/* tree is initially empty. Set up new node to match Xkey[0] */
Xmap = GetFreeNode(Xkey[0]); /* it is properly initialized */
/* Now recurse through Xmap */
(void) TryNode(Xmap, Xkey, code, 1); /* string */
return;
}
void
AddXKeyCmd(Xkey, CmdCode)
Char *Xkey;
int CmdCode;
{
/* Gould does not like casts... */
unsigned int comp_r_bug;
if (Xkey[0] == '\0') {
xprintf("AddXKeyCmd: Null extended-key not allowed.\n");
return;
}
if (CmdCode == F_XKEY) {
xprintf("AddXKeyCmd: sequence-lead-in command not allowed\n");
return;
}
if (Xmap == NULL)
/* tree is initially empty. Set up new node to match Xkey[0] */
Xmap = GetFreeNode(Xkey[0]); /* it is properly initialized */
/* Now recurse through Xmap */
comp_r_bug = (unsigned int) CmdCode;
(void) TryNode(Xmap, Xkey, (Char *) comp_r_bug, 0); /* command */
return;
}
static int
TryNode(ptr, string, code, IsString)
XmapNode *ptr;
Char *string;
Char *code;
int IsString;
{
/*
* Find a node that matches *string or allocate a new one
*/
if (ptr->ch != *string) {
XmapNode *xm;
for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
if (xm->sibling->ch == *string)
break;
if (xm->sibling == NULL)
xm->sibling = GetFreeNode(*string); /* setup new node */
ptr = xm->sibling;
}
if (*++string == '\0') {
/* we're there */
if (ptr->next != NULL && ptr->next != &CurCmd) {
PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */
ptr->next = NULL;
}
if (ptr->next == NULL && ptr->code)
xfree((ptr_t) ptr->code);
if (IsString) {
ptr->next = NULL;
ptr->code = Strsave(code);
}
else {
ptr->next = &CurCmd;
ptr->code = code;
}
}
else {
/* still more chars to go */
/*
* christos: We need to allocate a new XmapNode also if the next
* XmapNode is the CurCmd, cause the previous XmapNode definition was
* only one char long!
*/
if (ptr->next == NULL || ptr->next == &CurCmd)
ptr->next = GetFreeNode(*string); /* setup new node */
(void) TryNode(ptr->next, string, code, IsString);
}
return (0);
}
void
ClearXkey(map, in)
KEYCMD *map;
Char *in;
{
if ((map[(unsigned char) *in] == F_XKEY) &&
((map == CcKeyMap && CcAltMap[(unsigned char) *in] != F_XKEY) ||
(map == CcAltMap && CcKeyMap[(unsigned char) *in] != F_XKEY)))
(void) DeleteXkey(in);
}
int
DeleteXkey(Xkey)
Char *Xkey;
{
if (Xkey[0] == '\0') {
xprintf("DeleteXkey: Null extended-key not allowed.\n");
return (-1);
}
if (Xmap == NULL)
return (0);
(void) TryDeleteNode(&Xmap, Xkey);
return (0);
}
static int
TryDeleteNode(inptr, string)
XmapNode **inptr;
Char *string;
{
XmapNode *ptr;
XmapNode *prev_ptr = NULL;
ptr = *inptr;
/*
* Find a node that matches *string or allocate a new one
*/
if (ptr->ch != *string) {
XmapNode *xm;
for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
if (xm->sibling->ch == *string)
break;
if (xm->sibling == NULL)
return (0);
prev_ptr = xm;
ptr = xm->sibling;
}
if (*++string == '\0') {
/* we're there */
if (prev_ptr == NULL)
*inptr = ptr->sibling;
else
prev_ptr->sibling = ptr->sibling;
ptr->sibling = NULL;
PutFreeNode(ptr);
return (1);
}
else if (ptr->next != NULL && TryDeleteNode(&ptr->next, string) == 1) {
if (ptr->next != NULL)
return (0);
if (prev_ptr == NULL)
*inptr = ptr->sibling;
else
prev_ptr->sibling = ptr->sibling;
ptr->sibling = NULL;
PutFreeNode(ptr);
return (1);
}
else {
return (0);
}
}
/* PutFreeNode():
* Puts a tree of nodes onto free list using free(3).
*/
static void
PutFreeNode(ptr)
XmapNode *ptr;
{
if (ptr == NULL)
return;
if (ptr->next && ptr->next != &CurCmd)
PutFreeNode(ptr->next);
PutFreeNode(ptr->sibling);
if (ptr->next == NULL && ptr->code)
xfree((ptr_t) ptr->code);
xfree((ptr_t) ptr);
}
/* GetFreeNode():
* Returns pointer to an XmapNode for ch.
*/
static XmapNode *
GetFreeNode(ch)
int ch;
{
XmapNode *ptr;
ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode));
ptr->ch = ch;
ptr->code = NULL;
ptr->next = NULL;
ptr->sibling = NULL;
return (ptr);
}
/* Now The Print Routine */
#define maxXkey 100 /* max length of a Xkey for print putposes */
static Char printbuf[maxXkey]; /* buffer for printing */
static int Lookup();
static int Enumerate();
static int printOne();
static int u_p_ch();
extern unsigned char *u_p_string();
/* PrintXKey():
* Print the binding associated with Xkey key.
* Print entire Xmap if null
*/
void
PrintXkey(key)
Char *key;
{
/* do nothing if Xmap is empty and null key specified */
if (Xmap == NULL && *key == 0)
return;
printbuf[0] = '"';
if (Lookup(key, Xmap, 1) <= -1)
/* key is not bound */
xprintf("Unbound extended key \"%s\"\n", short2str(key));
return;
}
/* Lookup():
* look for the string starting at node ptr.
* Print if last node
*/
static int
Lookup(string, ptr, cnt)
int cnt;
Char *string;
XmapNode *ptr;
{
int ncnt;
if (ptr == NULL)
return (-1); /* cannot have null ptr */
if (*string == 0) {
/* no more chars in string. Enumerate from here. */
(void) Enumerate(ptr, cnt);
return (0);
}
else {
/* If match put this char into printbuf. Recurse */
if (ptr->ch == *string) {
/* match found */
ncnt = u_p_ch(cnt, ptr->ch);
if (ptr->next && ptr->next != &CurCmd)
/* not yet at leaf */
return (Lookup(string + 1, ptr->next, ncnt + 1));
else {
/* next node is null or &CurCmd so key should be complete */
if (string[1] == 0) {
printbuf[ncnt + 1] = '"';
printbuf[ncnt + 2] = '\0';
(void) printOne(printbuf, ptr->code, ptr->next == NULL);
return (0);
}
else
return (-1);/* mismatch -- string still has chars */
}
}
else {
/* no match found try sibling */
if (ptr->sibling)
return (Lookup(string, ptr->sibling, cnt));
else
return (-1);
}
}
}
static int
Enumerate(ptr, cnt)
int cnt;
XmapNode *ptr;
{
int ncnt;
if (cnt >= maxXkey - 5) { /* buffer too small */
printbuf[++cnt] = '"';
printbuf[++cnt] = '\0';
xprintf("Some extended keys too long for internal print buffer");
xprintf(" \"%s...\"\n", short2str(printbuf));
return (0);
}
if (ptr == NULL) {
#ifdef DEBUG_EDIT
xprintf("Enumerate: BUG!! Null ptr passed\n!");
#endif
return (-1);
}
ncnt = u_p_ch(cnt, ptr->ch); /* put this char at end of string */
if (ptr->next == NULL || ptr->next == &CurCmd) {
/* print this Xkey and function */
printbuf[ncnt + 1] = '"';
printbuf[ncnt + 2] = '\0';
(void) printOne(printbuf, ptr->code, ptr->next == NULL);
}
else
(void) Enumerate(ptr->next, ncnt + 1);
/* go to sibling if there is one */
if (ptr->sibling)
(void) Enumerate(ptr->sibling, cnt);
return (0);
}
/* PrintOne():
* Print the specified key and its associated
* function specified by code
*/
static int
printOne(key, code, prstring)
Char *key;
Char *code;
int prstring;
{
struct KeyFuncs *fp;
unsigned char unparsbuf[200];
static char *fmt = "%-15s-> %s\n";
if (code) {
if (prstring)
xprintf(fmt, short2str(key), u_p_string(code, unparsbuf));
else {
for (fp = FuncNames; fp->name; fp++) {
if ((int) code == fp->func)
xprintf(fmt, short2str(key), fp->name);
}
}
}
else
xprintf(fmt, short2str(key), "no input");
return (0);
}
static int
u_p_ch(cnt, ch)
int cnt;
Char ch;
{
if (ch == 0) {
printbuf[cnt++] = '^';
printbuf[cnt] = '@';
return cnt;
}
if (Iscntrl(ch)) {
printbuf[cnt++] = '^';
if (ch == '\177')
printbuf[cnt] = '?';
else
printbuf[cnt] = ch | 0100;
}
else if (ch == '^') {
printbuf[cnt++] = '\\';
printbuf[cnt] = '^';
}
else if (ch == '\\') {
printbuf[cnt++] = '\\';
printbuf[cnt] = '\\';
}
else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
printbuf[cnt] = ch;
}
else {
printbuf[cnt++] = '\\';
printbuf[cnt++] = ((ch >> 6) & 7) + '0';
printbuf[cnt++] = ((ch >> 3) & 7) + '0';
printbuf[cnt] = (ch & 7) + '0';
}
return cnt;
}