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