ed.xmap.c revision 100616
1100616Smp/* $Header: /src/pub/tcsh/ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $ */
259243Sobrien/*
359243Sobrien * ed.xmap.c: This module contains the procedures for maintaining
459243Sobrien *	      the extended-key map.
559243Sobrien *
659243Sobrien * 	      An extended-key (Xkey) is a sequence of keystrokes
759243Sobrien *	      introduced with an sequence introducer and consisting
859243Sobrien *	      of an arbitrary number of characters.  This module maintains
959243Sobrien *	      a map (the Xmap) to convert these extended-key sequences
1059243Sobrien * 	      into input strings (XK_STR), editor functions (XK_CMD), or
1159243Sobrien *	      unix commands (XK_EXE). It contains the
1259243Sobrien *	      following externally visible functions.
1359243Sobrien *
1459243Sobrien *		int GetXkey(ch,val);
1559243Sobrien *		CStr *ch;
1659243Sobrien *		XmapVal *val;
1759243Sobrien *
1859243Sobrien *	      Looks up *ch in map and then reads characters until a
1959243Sobrien *	      complete match is found or a mismatch occurs. Returns the
2059243Sobrien *	      type of the match found (XK_STR, XK_CMD, or XK_EXE).
2159243Sobrien *	      Returns NULL in val.str and XK_STR for no match.
2259243Sobrien *	      The last character read is returned in *ch.
2359243Sobrien *
2459243Sobrien *		void AddXkey(Xkey, val, ntype);
2559243Sobrien *		CStr *Xkey;
2659243Sobrien *		XmapVal *val;
2759243Sobrien *		int ntype;
2859243Sobrien *
2959243Sobrien *	      Adds Xkey to the Xmap and associates the value in val with it.
3059243Sobrien *	      If Xkey is already is in Xmap, the new code is applied to the
3159243Sobrien *	      existing Xkey. Ntype specifies if code is a command, an
3259243Sobrien *	      out string or a unix command.
3359243Sobrien *
3459243Sobrien *	        int DeleteXkey(Xkey);
3559243Sobrien *	        CStr *Xkey;
3659243Sobrien *
3759243Sobrien *	      Delete the Xkey and all longer Xkeys staring with Xkey, if
3859243Sobrien *	      they exists.
3959243Sobrien *
4059243Sobrien *	      Warning:
4159243Sobrien *		If Xkey is a substring of some other Xkeys, then the longer
4259243Sobrien *		Xkeys are lost!!  That is, if the Xkeys "abcd" and "abcef"
4359243Sobrien *		are in Xmap, adding the key "abc" will cause the first two
4459243Sobrien *		definitions to be lost.
4559243Sobrien *
4659243Sobrien *		void ResetXmap();
4759243Sobrien *
4859243Sobrien *	      Removes all entries from Xmap and resets the defaults.
4959243Sobrien *
5059243Sobrien *		void PrintXkey(Xkey);
5159243Sobrien *		CStr *Xkey;
5259243Sobrien *
5359243Sobrien *	      Prints all extended keys prefixed by Xkey and their associated
5459243Sobrien *	      commands.
5559243Sobrien *
5659243Sobrien *	      Restrictions:
5759243Sobrien *	      -------------
5859243Sobrien *	        1) It is not possible to have one Xkey that is a
5959243Sobrien *		   substring of another.
6059243Sobrien */
6159243Sobrien/*-
6259243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
6359243Sobrien * All rights reserved.
6459243Sobrien *
6559243Sobrien * Redistribution and use in source and binary forms, with or without
6659243Sobrien * modification, are permitted provided that the following conditions
6759243Sobrien * are met:
6859243Sobrien * 1. Redistributions of source code must retain the above copyright
6959243Sobrien *    notice, this list of conditions and the following disclaimer.
7059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
7159243Sobrien *    notice, this list of conditions and the following disclaimer in the
7259243Sobrien *    documentation and/or other materials provided with the distribution.
73100616Smp * 3. Neither the name of the University nor the names of its contributors
7459243Sobrien *    may be used to endorse or promote products derived from this software
7559243Sobrien *    without specific prior written permission.
7659243Sobrien *
7759243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
7859243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
7959243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
8059243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
8159243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
8259243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
8359243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8459243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
8559243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
8659243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
8759243Sobrien * SUCH DAMAGE.
8859243Sobrien */
8959243Sobrien#include "sh.h"
9059243Sobrien
91100616SmpRCSID("$Id: ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $")
9259243Sobrien
9359243Sobrien#include "ed.h"
9459243Sobrien#include "ed.defns.h"
9559243Sobrien
9659243Sobrien#ifndef NULL
9759243Sobrien#define NULL 0
9859243Sobrien#endif
9959243Sobrien
10059243Sobrien/* Internal Data types and declarations */
10159243Sobrien
10259243Sobrien/* The Nodes of the Xmap.  The Xmap is a linked list of these node
10359243Sobrien * elements
10459243Sobrien */
10559243Sobrientypedef struct Xmapnode {
10659243Sobrien    Char    ch;			/* single character of Xkey */
10759243Sobrien    int     type;
10859243Sobrien    XmapVal val; 		/* command code or pointer to string, if this
10959243Sobrien				 * is a leaf */
11059243Sobrien    struct Xmapnode *next;	/* ptr to next char of this Xkey */
11159243Sobrien    struct Xmapnode *sibling;	/* ptr to another Xkey with same prefix */
11259243Sobrien} XmapNode;
11359243Sobrien
11459243Sobrienstatic XmapNode *Xmap = NULL;	/* the current Xmap */
11559243Sobrien#define MAXXKEY 100		/* max length of a Xkey for print putposes */
11659243Sobrienstatic Char printbuf[MAXXKEY];	/* buffer for printing */
11759243Sobrien
11859243Sobrien
11959243Sobrien/* Some declarations of procedures */
12059243Sobrienstatic	int       TraverseMap	__P((XmapNode *, CStr *, XmapVal *));
12159243Sobrienstatic	int       TryNode	__P((XmapNode *, CStr *, XmapVal *, int));
12259243Sobrienstatic	XmapNode *GetFreeNode	__P((CStr *));
12359243Sobrienstatic	void	  PutFreeNode	__P((XmapNode *));
12459243Sobrienstatic	int	  TryDeleteNode	__P((XmapNode **, CStr *));
12559243Sobrienstatic	int	  Lookup	__P((CStr *, XmapNode *, int));
12659243Sobrienstatic	int	  Enumerate	__P((XmapNode *, int));
12759243Sobrienstatic	int	  unparsech	__P((int, Char *));
12859243Sobrien
12959243Sobrien
13059243SobrienXmapVal *
13159243SobrienXmapCmd(cmd)
13259243Sobrien    int cmd;
13359243Sobrien{
13459243Sobrien    static XmapVal xm;
13559243Sobrien    xm.cmd = (KEYCMD) cmd;
13659243Sobrien    return &xm;
13759243Sobrien}
13859243Sobrien
13959243SobrienXmapVal *
14059243SobrienXmapStr(str)
14159243Sobrien    CStr  *str;
14259243Sobrien{
14359243Sobrien    static XmapVal xm;
14459243Sobrien    xm.str.len = str->len;
14559243Sobrien    xm.str.buf = str->buf;
14659243Sobrien    return &xm;
14759243Sobrien}
14859243Sobrien
14959243Sobrien/* ResetXmap():
15059243Sobrien *	Takes all nodes on Xmap and puts them on free list.  Then
15159243Sobrien *	initializes Xmap with arrow keys
15259243Sobrien */
15359243Sobrienvoid
15459243SobrienResetXmap()
15559243Sobrien{
15659243Sobrien    PutFreeNode(Xmap);
15759243Sobrien    Xmap = NULL;
15859243Sobrien
15959243Sobrien    DefaultArrowKeys();
16059243Sobrien    return;
16159243Sobrien}
16259243Sobrien
16359243Sobrien
16459243Sobrien/* GetXkey():
16559243Sobrien *	Calls the recursive function with entry point Xmap
16659243Sobrien */
16759243Sobrienint
16859243SobrienGetXkey(ch, val)
16959243Sobrien    CStr     *ch;
17059243Sobrien    XmapVal  *val;
17159243Sobrien{
17259243Sobrien    return (TraverseMap(Xmap, ch, val));
17359243Sobrien}
17459243Sobrien
17559243Sobrien/* TraverseMap():
17659243Sobrien *	recursively traverses node in tree until match or mismatch is
17759243Sobrien * 	found.  May read in more characters.
17859243Sobrien */
17959243Sobrienstatic int
18059243SobrienTraverseMap(ptr, ch, val)
18159243Sobrien    XmapNode *ptr;
18259243Sobrien    CStr     *ch;
18359243Sobrien    XmapVal  *val;
18459243Sobrien{
18559243Sobrien    Char    tch;
18659243Sobrien
18759243Sobrien    if (ptr->ch == *(ch->buf)) {
18859243Sobrien	/* match found */
18959243Sobrien	if (ptr->next) {
19059243Sobrien	    /* Xkey not complete so get next char */
19159243Sobrien	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
19259243Sobrien		val->cmd = F_SEND_EOF;
19359243Sobrien		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
19459243Sobrien	    }
19559243Sobrien	    *(ch->buf) = tch;
19659243Sobrien	    return (TraverseMap(ptr->next, ch, val));
19759243Sobrien	}
19859243Sobrien	else {
19959243Sobrien	    *val = ptr->val;
20059243Sobrien	    if (ptr->type != XK_CMD)
20159243Sobrien		*(ch->buf) = '\0';
20259243Sobrien	    return ptr->type;
20359243Sobrien	}
20459243Sobrien    }
20559243Sobrien    else {
20659243Sobrien	/* no match found here */
20759243Sobrien	if (ptr->sibling) {
20859243Sobrien	    /* try next sibling */
20959243Sobrien	    return (TraverseMap(ptr->sibling, ch, val));
21059243Sobrien	}
21159243Sobrien	else {
21259243Sobrien	    /* no next sibling -- mismatch */
21359243Sobrien	    val->str.buf = NULL;
21459243Sobrien	    val->str.len = 0;
21559243Sobrien	    return XK_STR;
21659243Sobrien	}
21759243Sobrien    }
21859243Sobrien}
21959243Sobrien
22059243Sobrienvoid
22159243SobrienAddXkey(Xkey, val, ntype)
22259243Sobrien    CStr    *Xkey;
22359243Sobrien    XmapVal *val;
22459243Sobrien    int      ntype;
22559243Sobrien{
22659243Sobrien    CStr cs;
22759243Sobrien    cs.buf = Xkey->buf;
22859243Sobrien    cs.len = Xkey->len;
22959243Sobrien    if (Xkey->len == 0) {
23059243Sobrien	xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
23159243Sobrien	return;
23259243Sobrien    }
23359243Sobrien
23459243Sobrien    if (ntype == XK_CMD && val->cmd == F_XKEY) {
23559243Sobrien	xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
23659243Sobrien	return;
23759243Sobrien    }
23859243Sobrien
23959243Sobrien    if (Xmap == NULL)
24059243Sobrien	/* tree is initially empty.  Set up new node to match Xkey[0] */
24159243Sobrien	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
24259243Sobrien
24359243Sobrien    /* Now recurse through Xmap */
24459243Sobrien    (void) TryNode(Xmap, &cs, val, ntype);
24559243Sobrien    return;
24659243Sobrien}
24759243Sobrien
24859243Sobrienstatic int
24959243SobrienTryNode(ptr, str, val, ntype)
25059243Sobrien    XmapNode *ptr;
25159243Sobrien    CStr     *str;
25259243Sobrien    XmapVal  *val;
25359243Sobrien    int       ntype;
25459243Sobrien{
25559243Sobrien    /*
25659243Sobrien     * Find a node that matches *string or allocate a new one
25759243Sobrien     */
25859243Sobrien    if (ptr->ch != *(str->buf)) {
25959243Sobrien	XmapNode *xm;
26059243Sobrien
26159243Sobrien	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
26259243Sobrien	    if (xm->sibling->ch == *(str->buf))
26359243Sobrien		break;
26459243Sobrien	if (xm->sibling == NULL)
26559243Sobrien	    xm->sibling = GetFreeNode(str);	/* setup new node */
26659243Sobrien	ptr = xm->sibling;
26759243Sobrien    }
26859243Sobrien
26959243Sobrien    str->buf++;
27059243Sobrien    str->len--;
27159243Sobrien    if (str->len == 0) {
27259243Sobrien	/* we're there */
27359243Sobrien	if (ptr->next != NULL) {
27459243Sobrien	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
27559243Sobrien	    ptr->next = NULL;
27659243Sobrien	}
27759243Sobrien
27859243Sobrien	switch (ptr->type) {
27959243Sobrien	case XK_STR:
28059243Sobrien	case XK_EXE:
28159243Sobrien	    if (ptr->val.str.buf != NULL)
28259243Sobrien		xfree((ptr_t) ptr->val.str.buf);
28359243Sobrien	    ptr->val.str.len = 0;
28459243Sobrien	    break;
28559243Sobrien	case XK_NOD:
28659243Sobrien	case XK_CMD:
28759243Sobrien	    break;
28859243Sobrien	default:
28959243Sobrien	    abort();
29059243Sobrien	    break;
29159243Sobrien	}
29259243Sobrien
29359243Sobrien	switch (ptr->type = ntype) {
29459243Sobrien	case XK_CMD:
29559243Sobrien	    ptr->val = *val;
29659243Sobrien	    break;
29759243Sobrien	case XK_STR:
29859243Sobrien	case XK_EXE:
29959243Sobrien	    ptr->val.str.len = (val->str.len + 1) * sizeof(Char);
30059243Sobrien	    ptr->val.str.buf = (Char *) xmalloc((size_t) ptr->val.str.len);
30159243Sobrien	    (void) memmove((ptr_t) ptr->val.str.buf, (ptr_t) val->str.buf,
30259243Sobrien			   (size_t) ptr->val.str.len);
30359243Sobrien	    ptr->val.str.len = val->str.len;
30459243Sobrien	    break;
30559243Sobrien	default:
30659243Sobrien	    abort();
30759243Sobrien	    break;
30859243Sobrien	}
30959243Sobrien    }
31059243Sobrien    else {
31159243Sobrien	/* still more chars to go */
31259243Sobrien	if (ptr->next == NULL)
31359243Sobrien	    ptr->next = GetFreeNode(str);	/* setup new node */
31459243Sobrien	(void) TryNode(ptr->next, str, val, ntype);
31559243Sobrien    }
31659243Sobrien    return (0);
31759243Sobrien}
31859243Sobrien
31959243Sobrienvoid
32059243SobrienClearXkey(map, in)
32159243Sobrien    KEYCMD *map;
32259243Sobrien    CStr   *in;
32359243Sobrien{
32459243Sobrien    unsigned char c = (unsigned char) *(in->buf);
32559243Sobrien    if ((map[c] == F_XKEY) &&
32659243Sobrien	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
32759243Sobrien	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
32859243Sobrien	(void) DeleteXkey(in);
32959243Sobrien}
33059243Sobrien
33159243Sobrienint
33259243SobrienDeleteXkey(Xkey)
33359243Sobrien    CStr   *Xkey;
33459243Sobrien{
33559243Sobrien    if (Xkey->len == 0) {
33659243Sobrien	xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
33759243Sobrien	return (-1);
33859243Sobrien    }
33959243Sobrien
34059243Sobrien    if (Xmap == NULL)
34159243Sobrien	return (0);
34259243Sobrien
34359243Sobrien    (void) TryDeleteNode(&Xmap, Xkey);
34459243Sobrien    return (0);
34559243Sobrien}
34659243Sobrien
34759243Sobrienstatic int
34859243SobrienTryDeleteNode(inptr, str)
34959243Sobrien    XmapNode **inptr;
35059243Sobrien    CStr   *str;
35159243Sobrien{
35259243Sobrien    XmapNode *ptr;
35359243Sobrien    XmapNode *prev_ptr = NULL;
35459243Sobrien
35559243Sobrien    ptr = *inptr;
35659243Sobrien    /*
35759243Sobrien     * Find a node that matches *string or allocate a new one
35859243Sobrien     */
35959243Sobrien    if (ptr->ch != *(str->buf)) {
36059243Sobrien	XmapNode *xm;
36159243Sobrien
36259243Sobrien	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
36359243Sobrien	    if (xm->sibling->ch == *(str->buf))
36459243Sobrien		break;
36559243Sobrien	if (xm->sibling == NULL)
36659243Sobrien	    return (0);
36759243Sobrien	prev_ptr = xm;
36859243Sobrien	ptr = xm->sibling;
36959243Sobrien    }
37059243Sobrien
37159243Sobrien    str->buf++;
37259243Sobrien    str->len--;
37359243Sobrien
37459243Sobrien    if (str->len == 0) {
37559243Sobrien	/* we're there */
37659243Sobrien	if (prev_ptr == NULL)
37759243Sobrien	    *inptr = ptr->sibling;
37859243Sobrien	else
37959243Sobrien	    prev_ptr->sibling = ptr->sibling;
38059243Sobrien	ptr->sibling = NULL;
38159243Sobrien	PutFreeNode(ptr);
38259243Sobrien	return (1);
38359243Sobrien    }
38459243Sobrien    else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
38559243Sobrien	if (ptr->next != NULL)
38659243Sobrien	    return (0);
38759243Sobrien	if (prev_ptr == NULL)
38859243Sobrien	    *inptr = ptr->sibling;
38959243Sobrien	else
39059243Sobrien	    prev_ptr->sibling = ptr->sibling;
39159243Sobrien	ptr->sibling = NULL;
39259243Sobrien	PutFreeNode(ptr);
39359243Sobrien	return (1);
39459243Sobrien    }
39559243Sobrien    else {
39659243Sobrien	return (0);
39759243Sobrien    }
39859243Sobrien}
39959243Sobrien
40059243Sobrien/* PutFreeNode():
40159243Sobrien *	Puts a tree of nodes onto free list using free(3).
40259243Sobrien */
40359243Sobrienstatic void
40459243SobrienPutFreeNode(ptr)
40559243Sobrien    XmapNode *ptr;
40659243Sobrien{
40759243Sobrien    if (ptr == NULL)
40859243Sobrien	return;
40959243Sobrien
41059243Sobrien    if (ptr->next != NULL) {
41159243Sobrien	PutFreeNode(ptr->next);
41259243Sobrien	ptr->next = NULL;
41359243Sobrien    }
41459243Sobrien
41559243Sobrien    PutFreeNode(ptr->sibling);
41659243Sobrien
41759243Sobrien    switch (ptr->type) {
41859243Sobrien    case XK_CMD:
41959243Sobrien    case XK_NOD:
42059243Sobrien	break;
42159243Sobrien    case XK_EXE:
42259243Sobrien    case XK_STR:
42359243Sobrien	if (ptr->val.str.buf != NULL)
42459243Sobrien	    xfree((ptr_t) ptr->val.str.buf);
42559243Sobrien	break;
42659243Sobrien    default:
42759243Sobrien	abort();
42859243Sobrien	break;
42959243Sobrien    }
43059243Sobrien    xfree((ptr_t) ptr);
43159243Sobrien}
43259243Sobrien
43359243Sobrien
43459243Sobrien/* GetFreeNode():
43559243Sobrien *	Returns pointer to an XmapNode for ch.
43659243Sobrien */
43759243Sobrienstatic XmapNode *
43859243SobrienGetFreeNode(ch)
43959243Sobrien    CStr *ch;
44059243Sobrien{
44159243Sobrien    XmapNode *ptr;
44259243Sobrien
44359243Sobrien    ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode));
44459243Sobrien    ptr->ch = ch->buf[0];
44559243Sobrien    ptr->type = XK_NOD;
44659243Sobrien    ptr->val.str.buf = NULL;
44759243Sobrien    ptr->val.str.len = 0;
44859243Sobrien    ptr->next = NULL;
44959243Sobrien    ptr->sibling = NULL;
45059243Sobrien    return (ptr);
45159243Sobrien}
45259243Sobrien
45359243Sobrien
45459243Sobrien/* PrintXKey():
45559243Sobrien *	Print the binding associated with Xkey key.
45659243Sobrien *	Print entire Xmap if null
45759243Sobrien */
45859243Sobrienvoid
45959243SobrienPrintXkey(key)
46059243Sobrien    CStr   *key;
46159243Sobrien{
46259243Sobrien    CStr cs;
46359243Sobrien
46459243Sobrien    if (key) {
46559243Sobrien	cs.buf = key->buf;
46659243Sobrien	cs.len = key->len;
46759243Sobrien    }
46859243Sobrien    else {
46959243Sobrien	cs.buf = STRNULL;
47059243Sobrien	cs.len = 0;
47159243Sobrien    }
47259243Sobrien    /* do nothing if Xmap is empty and null key specified */
47359243Sobrien    if (Xmap == NULL && cs.len == 0)
47459243Sobrien	return;
47559243Sobrien
47659243Sobrien    printbuf[0] =  '"';
47759243Sobrien    if (Lookup(&cs, Xmap, 1) <= -1)
47859243Sobrien	/* key is not bound */
47959243Sobrien	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
48059243Sobrien    return;
48159243Sobrien}
48259243Sobrien
48359243Sobrien/* Lookup():
48459243Sobrien *	look for the string starting at node ptr.
48559243Sobrien *	Print if last node
48659243Sobrien */
48759243Sobrienstatic int
48859243SobrienLookup(str, ptr, cnt)
48959243Sobrien    CStr   *str;
49059243Sobrien    XmapNode *ptr;
49159243Sobrien    int     cnt;
49259243Sobrien{
49359243Sobrien    int     ncnt;
49459243Sobrien
49559243Sobrien    if (ptr == NULL)
49659243Sobrien	return (-1);		/* cannot have null ptr */
49759243Sobrien
49859243Sobrien    if (str->len == 0) {
49959243Sobrien	/* no more chars in string.  Enumerate from here. */
50059243Sobrien	(void) Enumerate(ptr, cnt);
50159243Sobrien	return (0);
50259243Sobrien    }
50359243Sobrien    else {
50459243Sobrien	/* If match put this char into printbuf.  Recurse */
50559243Sobrien	if (ptr->ch == *(str->buf)) {
50659243Sobrien	    /* match found */
50759243Sobrien	    ncnt = unparsech(cnt, &ptr->ch);
50859243Sobrien	    if (ptr->next != NULL) {
50959243Sobrien		/* not yet at leaf */
51059243Sobrien		CStr tstr;
51159243Sobrien		tstr.buf = str->buf + 1;
51259243Sobrien		tstr.len = str->len - 1;
51359243Sobrien		return (Lookup(&tstr, ptr->next, ncnt + 1));
51459243Sobrien	    }
51559243Sobrien	    else {
51659243Sobrien		/* next node is null so key should be complete */
51759243Sobrien		if (str->len == 1) {
51859243Sobrien		    CStr pb;
51959243Sobrien		    printbuf[ncnt + 1] = '"';
52059243Sobrien		    printbuf[ncnt + 2] = '\0';
52159243Sobrien		    pb.buf = printbuf;
52259243Sobrien		    pb.len = ncnt + 2;
52359243Sobrien		    (void) printOne(&pb, &ptr->val, ptr->type);
52459243Sobrien		    return (0);
52559243Sobrien		}
52659243Sobrien		else
52759243Sobrien		    return (-1);/* mismatch -- string still has chars */
52859243Sobrien	    }
52959243Sobrien	}
53059243Sobrien	else {
53159243Sobrien	    /* no match found try sibling */
53259243Sobrien	    if (ptr->sibling)
53359243Sobrien		return (Lookup(str, ptr->sibling, cnt));
53459243Sobrien	    else
53559243Sobrien		return (-1);
53659243Sobrien	}
53759243Sobrien    }
53859243Sobrien}
53959243Sobrien
54059243Sobrienstatic int
54159243SobrienEnumerate(ptr, cnt)
54259243Sobrien    XmapNode *ptr;
54359243Sobrien    int     cnt;
54459243Sobrien{
54559243Sobrien    int     ncnt;
54659243Sobrien
54759243Sobrien    if (cnt >= MAXXKEY - 5) {	/* buffer too small */
54859243Sobrien	printbuf[++cnt] = '"';
54959243Sobrien	printbuf[++cnt] = '\0';
55059243Sobrien	xprintf(CGETS(9, 5,
55159243Sobrien		"Some extended keys too long for internal print buffer"));
55259243Sobrien	xprintf(" \"%S...\"\n", printbuf);
55359243Sobrien	return (0);
55459243Sobrien    }
55559243Sobrien
55659243Sobrien    if (ptr == NULL) {
55759243Sobrien#ifdef DEBUG_EDIT
55859243Sobrien	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
55959243Sobrien#endif
56059243Sobrien	return (-1);
56159243Sobrien    }
56259243Sobrien
56359243Sobrien    ncnt = unparsech(cnt, &ptr->ch); /* put this char at end of string */
56459243Sobrien    if (ptr->next == NULL) {
56559243Sobrien	CStr pb;
56659243Sobrien	/* print this Xkey and function */
56759243Sobrien	printbuf[++ncnt] = '"';
56859243Sobrien	printbuf[++ncnt] = '\0';
56959243Sobrien	pb.buf = printbuf;
57059243Sobrien	pb.len = ncnt;
57159243Sobrien	(void) printOne(&pb, &ptr->val, ptr->type);
57259243Sobrien    }
57359243Sobrien    else
57459243Sobrien	(void) Enumerate(ptr->next, ncnt + 1);
57559243Sobrien
57659243Sobrien    /* go to sibling if there is one */
57759243Sobrien    if (ptr->sibling)
57859243Sobrien	(void) Enumerate(ptr->sibling, cnt);
57959243Sobrien    return (0);
58059243Sobrien}
58159243Sobrien
58259243Sobrien
58359243Sobrien/* PrintOne():
58459243Sobrien *	Print the specified key and its associated
58559243Sobrien *	function specified by val
58659243Sobrien */
58759243Sobrienint
58859243SobrienprintOne(key, val, ntype)
58959243Sobrien    CStr    *key;
59059243Sobrien    XmapVal *val;
59159243Sobrien    int      ntype;
59259243Sobrien{
59359243Sobrien    struct KeyFuncs *fp;
59459243Sobrien    unsigned char unparsbuf[200];
59559243Sobrien    static char *fmt = "%s\n";
59659243Sobrien
59759243Sobrien    xprintf("%-15S-> ", key->buf);
59859243Sobrien    if (val != NULL)
59959243Sobrien	switch (ntype) {
60059243Sobrien	case XK_STR:
60159243Sobrien	case XK_EXE:
60259243Sobrien	    xprintf(fmt, unparsestring(&val->str, unparsbuf,
60359243Sobrien				       ntype == XK_STR ? STRQQ : STRBB));
60459243Sobrien	    break;
60559243Sobrien	case XK_CMD:
60659243Sobrien	    for (fp = FuncNames; fp->name; fp++)
60759243Sobrien		if (val->cmd == fp->func)
60859243Sobrien		    xprintf(fmt, fp->name);
60959243Sobrien		break;
61059243Sobrien	default:
61159243Sobrien	    abort();
61259243Sobrien	    break;
61359243Sobrien	}
61459243Sobrien    else
61559243Sobrien	xprintf(fmt, key, CGETS(9, 7, "no input"));
61659243Sobrien    return (0);
61759243Sobrien}
61859243Sobrien
61959243Sobrienstatic int
62059243Sobrienunparsech(cnt, ch)
62159243Sobrien    int   cnt;
62259243Sobrien    Char  *ch;
62359243Sobrien{
62459243Sobrien    if (ch == 0) {
62559243Sobrien	printbuf[cnt++] = '^';
62659243Sobrien	printbuf[cnt] = '@';
62759243Sobrien	return cnt;
62859243Sobrien    }
62959243Sobrien
63059243Sobrien    if (Iscntrl(*ch)) {
63169408Sache#ifdef IS_ASCII
63259243Sobrien	printbuf[cnt++] = '^';
63359243Sobrien	if (*ch == CTL_ESC('\177'))
63459243Sobrien	    printbuf[cnt] = '?';
63559243Sobrien	else
63659243Sobrien	    printbuf[cnt] = *ch | 0100;
63769408Sache#else
63859243Sobrien	if (*ch == CTL_ESC('\177'))
63959243Sobrien	{
64059243Sobrien		printbuf[cnt++] = '^';
64159243Sobrien		printbuf[cnt] = '?';
64259243Sobrien	}
64359243Sobrien	else if (Isupper(_toebcdic[_toascii[*ch]|0100])
64459243Sobrien		|| strchr("@[\\]^_", _toebcdic[_toascii[*ch]|0100]) != NULL)
64559243Sobrien	{
64659243Sobrien		printbuf[cnt++] = '^';
64759243Sobrien		printbuf[cnt] = _toebcdic[_toascii[*ch]|0100];
64859243Sobrien	}
64959243Sobrien	else
65059243Sobrien	{
65159243Sobrien		printbuf[cnt++] = '\\';
65259243Sobrien		printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
65359243Sobrien		printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
65459243Sobrien		printbuf[cnt] = (*ch & 7) + '0';
65559243Sobrien	}
65669408Sache#endif
65759243Sobrien    }
65859243Sobrien    else if (*ch == '^') {
65959243Sobrien	printbuf[cnt++] = '\\';
66059243Sobrien	printbuf[cnt] = '^';
66159243Sobrien    }
66259243Sobrien    else if (*ch == '\\') {
66359243Sobrien	printbuf[cnt++] = '\\';
66459243Sobrien	printbuf[cnt] = '\\';
66559243Sobrien    }
66659243Sobrien    else if (*ch == ' ' || (Isprint(*ch) && !Isspace(*ch))) {
66759243Sobrien	printbuf[cnt] = *ch;
66859243Sobrien    }
66959243Sobrien    else {
67059243Sobrien	printbuf[cnt++] = '\\';
67159243Sobrien	printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
67259243Sobrien	printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
67359243Sobrien	printbuf[cnt] = (*ch & 7) + '0';
67459243Sobrien    }
67559243Sobrien    return cnt;
67659243Sobrien}
67759243Sobrien
67859243Sobrienint
67959243Sobrienparseescape(ptr)
68059243Sobrien    const Char  **ptr;
68159243Sobrien{
68259243Sobrien    const Char *p;
68359243Sobrien    Char c;
68459243Sobrien
68559243Sobrien    p = *ptr;
68659243Sobrien
68759243Sobrien    if ((p[1] & CHAR) == 0) {
68859243Sobrien	xprintf(CGETS(9, 8, "Something must follow: %c\n"), *p);
68959243Sobrien	return -1;
69059243Sobrien    }
69159243Sobrien    if ((*p & CHAR) == '\\') {
69259243Sobrien	p++;
69359243Sobrien	switch (*p & CHAR) {
69459243Sobrien	case 'a':
69559243Sobrien	    c = CTL_ESC('\007');         /* Bell */
69659243Sobrien	    break;
69759243Sobrien	case 'b':
69859243Sobrien	    c = CTL_ESC('\010');         /* Backspace */
69959243Sobrien	    break;
70059243Sobrien	case 'e':
70159243Sobrien	    c = CTL_ESC('\033');         /* Escape */
70259243Sobrien	    break;
70359243Sobrien	case 'f':
70459243Sobrien	    c = CTL_ESC('\014');         /* Form Feed */
70559243Sobrien	    break;
70659243Sobrien	case 'n':
70759243Sobrien	    c = CTL_ESC('\012');         /* New Line */
70859243Sobrien	    break;
70959243Sobrien	case 'r':
71059243Sobrien	    c = CTL_ESC('\015');         /* Carriage Return */
71159243Sobrien	    break;
71259243Sobrien	case 't':
71359243Sobrien	    c = CTL_ESC('\011');         /* Horizontal Tab */
71459243Sobrien	    break;
71559243Sobrien	case 'v':
71659243Sobrien	    c = CTL_ESC('\013');         /* Vertical Tab */
71759243Sobrien	    break;
71883098Smp	case '\\':
71983098Smp	    c = '\\';
72083098Smp	    break;
72159243Sobrien	case '0':
72259243Sobrien	case '1':
72359243Sobrien	case '2':
72459243Sobrien	case '3':
72559243Sobrien	case '4':
72659243Sobrien	case '5':
72759243Sobrien	case '6':
72859243Sobrien	case '7':
72959243Sobrien	    {
73059243Sobrien		register int cnt, val, ch;
73159243Sobrien
73259243Sobrien		for (cnt = 0, val = 0; cnt < 3; cnt++) {
73359243Sobrien		    ch = *p++ & CHAR;
73459243Sobrien		    if (ch < '0' || ch > '7') {
73559243Sobrien			p--;
73659243Sobrien			break;
73759243Sobrien		    }
73859243Sobrien		    val = (val << 3) | (ch - '0');
73959243Sobrien		}
74059243Sobrien		if ((val & 0xffffff00) != 0) {
74159243Sobrien		    xprintf(CGETS(9, 9,
74259243Sobrien			    "Octal constant does not fit in a char.\n"));
74359243Sobrien		    return 0;
74459243Sobrien		}
74569408Sache#ifndef IS_ASCII
74659243Sobrien		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
74759243Sobrien		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
74859243Sobrien			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
74959243Sobrien#endif
75059243Sobrien		c = (Char) val;
75159243Sobrien		--p;
75259243Sobrien	    }
75359243Sobrien	    break;
75459243Sobrien	default:
75559243Sobrien	    c = *p;
75659243Sobrien	    break;
75759243Sobrien	}
75859243Sobrien    }
75959243Sobrien    else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
76059243Sobrien				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
76159243Sobrien	p++;
76269408Sache#ifdef IS_ASCII
76359243Sobrien	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
76469408Sache#else
76559243Sobrien	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
76659243Sobrien	if (adrof(STRwarnebcdic))
76759243Sobrien	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
76859243Sobrien		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
76969408Sache#endif
77059243Sobrien    }
77159243Sobrien    else
77259243Sobrien	c = *p;
77359243Sobrien    *ptr = p;
77459243Sobrien    return (c);
77559243Sobrien}
77659243Sobrien
77759243Sobrien
77859243Sobrienunsigned char *
77959243Sobrienunparsestring(str, buf, sep)
78059243Sobrien    CStr   *str;
78159243Sobrien    unsigned char *buf;
78259243Sobrien    Char   *sep;
78359243Sobrien{
78459243Sobrien    unsigned char *b;
78559243Sobrien    Char   p;
78659243Sobrien    int l;
78759243Sobrien
78859243Sobrien    b = buf;
78959243Sobrien    if (sep[0])
79069408Sache#ifndef WINNT_NATIVE
79159243Sobrien	*b++ = sep[0];
79269408Sache#else /* WINNT_NATIVE */
79359243Sobrien	*b++ = CHAR & sep[0];
79469408Sache#endif /* !WINNT_NATIVE */
79559243Sobrien
79659243Sobrien    for (l = 0; l < str->len; l++) {
79759243Sobrien	p = str->buf[l];
79859243Sobrien	if (Iscntrl(p)) {
79969408Sache#ifdef IS_ASCII
80059243Sobrien	    *b++ = '^';
80159243Sobrien	    if (p == CTL_ESC('\177'))
80259243Sobrien		*b++ = '?';
80359243Sobrien	    else
80459243Sobrien		*b++ = (unsigned char) (p | 0100);
80569408Sache#else
80659243Sobrien	    if (_toascii[p] == '\177' || Isupper(_toebcdic[_toascii[p]|0100])
80759243Sobrien		 || strchr("@[\\]^_", _toebcdic[_toascii[p]|0100]) != NULL)
80859243Sobrien	    {
80959243Sobrien		*b++ = '^';
81059243Sobrien		*b++ = (_toascii[p] == '\177') ? '?' : _toebcdic[_toascii[p]|0100];
81159243Sobrien	    }
81259243Sobrien	    else
81359243Sobrien	    {
81459243Sobrien		*b++ = '\\';
81559243Sobrien		*b++ = ((p >> 6) & 7) + '0';
81659243Sobrien		*b++ = ((p >> 3) & 7) + '0';
81759243Sobrien		*b++ = (p & 7) + '0';
81859243Sobrien	    }
81969408Sache#endif
82059243Sobrien	}
82159243Sobrien	else if (p == '^' || p == '\\') {
82259243Sobrien	    *b++ = '\\';
82359243Sobrien	    *b++ = (unsigned char) p;
82459243Sobrien	}
82559243Sobrien	else if (p == ' ' || (Isprint(p) && !Isspace(p))) {
82659243Sobrien	    *b++ = (unsigned char) p;
82759243Sobrien	}
82859243Sobrien	else {
82959243Sobrien	    *b++ = '\\';
83059243Sobrien	    *b++ = ((p >> 6) & 7) + '0';
83159243Sobrien	    *b++ = ((p >> 3) & 7) + '0';
83259243Sobrien	    *b++ = (p & 7) + '0';
83359243Sobrien	}
83459243Sobrien    }
83559243Sobrien    if (sep[0] && sep[1])
83669408Sache#ifndef WINNT_NATIVE
83759243Sobrien	*b++ = sep[1];
83869408Sache#else /* WINNT_NATIVE */
83959243Sobrien	*b++ = CHAR & sep[1];
84069408Sache#endif /* !WINNT_NATIVE */
84159243Sobrien    *b++ = 0;
84259243Sobrien    return buf;			/* should check for overflow */
84359243Sobrien}
844