1316958Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.38 2015/06/06 21:19:07 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
91316958SdchaginRCSID("$tcsh: ed.xmap.c,v 3.38 2015/06/06 21:19:07 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
11659243Sobrien
11759243Sobrien/* Some declarations of procedures */
118167465Smpstatic	int       TraverseMap	(XmapNode *, CStr *, XmapVal *);
119167465Smpstatic	int       TryNode	(XmapNode *, CStr *, XmapVal *, int);
120167465Smpstatic	XmapNode *GetFreeNode	(CStr *);
121167465Smpstatic	void	  PutFreeNode	(XmapNode *);
122167465Smpstatic	int	  TryDeleteNode	(XmapNode **, CStr *);
123167465Smpstatic	int	  Lookup	(struct Strbuf *, const CStr *,
124167465Smp				 const XmapNode *);
125167465Smpstatic	void	  Enumerate	(struct Strbuf *, const XmapNode *);
126167465Smpstatic	void	  unparsech	(struct Strbuf *, Char);
12759243Sobrien
12859243Sobrien
12959243SobrienXmapVal *
130167465SmpXmapCmd(int cmd)
13159243Sobrien{
13259243Sobrien    static XmapVal xm;
13359243Sobrien    xm.cmd = (KEYCMD) cmd;
13459243Sobrien    return &xm;
13559243Sobrien}
13659243Sobrien
13759243SobrienXmapVal *
138167465SmpXmapStr(CStr *str)
13959243Sobrien{
14059243Sobrien    static XmapVal xm;
14159243Sobrien    xm.str.len = str->len;
14259243Sobrien    xm.str.buf = str->buf;
14359243Sobrien    return &xm;
14459243Sobrien}
14559243Sobrien
14659243Sobrien/* ResetXmap():
14759243Sobrien *	Takes all nodes on Xmap and puts them on free list.  Then
14859243Sobrien *	initializes Xmap with arrow keys
14959243Sobrien */
15059243Sobrienvoid
151167465SmpResetXmap(void)
15259243Sobrien{
15359243Sobrien    PutFreeNode(Xmap);
15459243Sobrien    Xmap = NULL;
15559243Sobrien
15659243Sobrien    DefaultArrowKeys();
15759243Sobrien    return;
15859243Sobrien}
15959243Sobrien
16059243Sobrien
16159243Sobrien/* GetXkey():
16259243Sobrien *	Calls the recursive function with entry point Xmap
16359243Sobrien */
16459243Sobrienint
165167465SmpGetXkey(CStr *ch, XmapVal *val)
16659243Sobrien{
16759243Sobrien    return (TraverseMap(Xmap, ch, val));
16859243Sobrien}
16959243Sobrien
17059243Sobrien/* TraverseMap():
17159243Sobrien *	recursively traverses node in tree until match or mismatch is
17259243Sobrien * 	found.  May read in more characters.
17359243Sobrien */
17459243Sobrienstatic int
175167465SmpTraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
17659243Sobrien{
17759243Sobrien    Char    tch;
17859243Sobrien
17959243Sobrien    if (ptr->ch == *(ch->buf)) {
18059243Sobrien	/* match found */
18159243Sobrien	if (ptr->next) {
18259243Sobrien	    /* Xkey not complete so get next char */
18359243Sobrien	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
18459243Sobrien		val->cmd = F_SEND_EOF;
18559243Sobrien		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
18659243Sobrien	    }
18759243Sobrien	    *(ch->buf) = tch;
18859243Sobrien	    return (TraverseMap(ptr->next, ch, val));
18959243Sobrien	}
19059243Sobrien	else {
19159243Sobrien	    *val = ptr->val;
19259243Sobrien	    if (ptr->type != XK_CMD)
19359243Sobrien		*(ch->buf) = '\0';
19459243Sobrien	    return ptr->type;
19559243Sobrien	}
19659243Sobrien    }
19759243Sobrien    else {
19859243Sobrien	/* no match found here */
19959243Sobrien	if (ptr->sibling) {
20059243Sobrien	    /* try next sibling */
20159243Sobrien	    return (TraverseMap(ptr->sibling, ch, val));
20259243Sobrien	}
20359243Sobrien	else {
20459243Sobrien	    /* no next sibling -- mismatch */
20559243Sobrien	    val->str.buf = NULL;
20659243Sobrien	    val->str.len = 0;
20759243Sobrien	    return XK_STR;
20859243Sobrien	}
20959243Sobrien    }
21059243Sobrien}
21159243Sobrien
21259243Sobrienvoid
213167465SmpAddXkey(const CStr *Xkey, XmapVal *val, int ntype)
21459243Sobrien{
21559243Sobrien    CStr cs;
21659243Sobrien    cs.buf = Xkey->buf;
21759243Sobrien    cs.len = Xkey->len;
21859243Sobrien    if (Xkey->len == 0) {
219195609Smp	xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
22059243Sobrien	return;
22159243Sobrien    }
22259243Sobrien
22359243Sobrien    if (ntype == XK_CMD && val->cmd == F_XKEY) {
224195609Smp	xprintf("%s",
225195609Smp	    CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
22659243Sobrien	return;
22759243Sobrien    }
22859243Sobrien
22959243Sobrien    if (Xmap == NULL)
23059243Sobrien	/* tree is initially empty.  Set up new node to match Xkey[0] */
23159243Sobrien	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
23259243Sobrien
23359243Sobrien    /* Now recurse through Xmap */
23459243Sobrien    (void) TryNode(Xmap, &cs, val, ntype);
23559243Sobrien    return;
23659243Sobrien}
23759243Sobrien
23859243Sobrienstatic int
239167465SmpTryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
24059243Sobrien{
24159243Sobrien    /*
24259243Sobrien     * Find a node that matches *string or allocate a new one
24359243Sobrien     */
24459243Sobrien    if (ptr->ch != *(str->buf)) {
24559243Sobrien	XmapNode *xm;
24659243Sobrien
24759243Sobrien	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
24859243Sobrien	    if (xm->sibling->ch == *(str->buf))
24959243Sobrien		break;
25059243Sobrien	if (xm->sibling == NULL)
25159243Sobrien	    xm->sibling = GetFreeNode(str);	/* setup new node */
25259243Sobrien	ptr = xm->sibling;
25359243Sobrien    }
25459243Sobrien
25559243Sobrien    str->buf++;
25659243Sobrien    str->len--;
25759243Sobrien    if (str->len == 0) {
258167465Smp	size_t len;
259167465Smp
26059243Sobrien	/* we're there */
26159243Sobrien	if (ptr->next != NULL) {
26259243Sobrien	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
26359243Sobrien	    ptr->next = NULL;
26459243Sobrien	}
26559243Sobrien
26659243Sobrien	switch (ptr->type) {
26759243Sobrien	case XK_STR:
26859243Sobrien	case XK_EXE:
269167465Smp	    xfree(ptr->val.str.buf);
27059243Sobrien	    ptr->val.str.len = 0;
27159243Sobrien	    break;
27259243Sobrien	case XK_NOD:
27359243Sobrien	case XK_CMD:
27459243Sobrien	    break;
27559243Sobrien	default:
27659243Sobrien	    abort();
27759243Sobrien	    break;
27859243Sobrien	}
27959243Sobrien
28059243Sobrien	switch (ptr->type = ntype) {
28159243Sobrien	case XK_CMD:
28259243Sobrien	    ptr->val = *val;
28359243Sobrien	    break;
28459243Sobrien	case XK_STR:
28559243Sobrien	case XK_EXE:
28659243Sobrien	    ptr->val.str.len = val->str.len;
287167465Smp	    len = (val->str.len + 1) * sizeof(*ptr->val.str.buf);
288167465Smp	    ptr->val.str.buf = xmalloc(len);
289167465Smp	    (void) memcpy(ptr->val.str.buf, val->str.buf, len);
29059243Sobrien	    break;
29159243Sobrien	default:
29259243Sobrien	    abort();
29359243Sobrien	    break;
29459243Sobrien	}
29559243Sobrien    }
29659243Sobrien    else {
29759243Sobrien	/* still more chars to go */
29859243Sobrien	if (ptr->next == NULL)
29959243Sobrien	    ptr->next = GetFreeNode(str);	/* setup new node */
30059243Sobrien	(void) TryNode(ptr->next, str, val, ntype);
30159243Sobrien    }
30259243Sobrien    return (0);
30359243Sobrien}
30459243Sobrien
30559243Sobrienvoid
306167465SmpClearXkey(KEYCMD *map, const CStr *in)
30759243Sobrien{
30859243Sobrien    unsigned char c = (unsigned char) *(in->buf);
30959243Sobrien    if ((map[c] == F_XKEY) &&
31059243Sobrien	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
31159243Sobrien	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
31259243Sobrien	(void) DeleteXkey(in);
31359243Sobrien}
31459243Sobrien
31559243Sobrienint
316167465SmpDeleteXkey(const CStr *Xkey)
31759243Sobrien{
318167465Smp    CStr s;
319167465Smp
320167465Smp    s = *Xkey;
321167465Smp    if (s.len == 0) {
322195609Smp	xprintf("%s",
323195609Smp	        CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
32459243Sobrien	return (-1);
32559243Sobrien    }
32659243Sobrien
32759243Sobrien    if (Xmap == NULL)
32859243Sobrien	return (0);
32959243Sobrien
330167465Smp    (void) TryDeleteNode(&Xmap, &s);
33159243Sobrien    return (0);
33259243Sobrien}
33359243Sobrien
334167465Smp/* Destroys str */
33559243Sobrienstatic int
336167465SmpTryDeleteNode(XmapNode **inptr, CStr *str)
33759243Sobrien{
33859243Sobrien    XmapNode *ptr;
33959243Sobrien
34059243Sobrien    ptr = *inptr;
34159243Sobrien    /*
34259243Sobrien     * Find a node that matches *string or allocate a new one
34359243Sobrien     */
34459243Sobrien    if (ptr->ch != *(str->buf)) {
34559243Sobrien	XmapNode *xm;
34659243Sobrien
34759243Sobrien	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
34859243Sobrien	    if (xm->sibling->ch == *(str->buf))
34959243Sobrien		break;
35059243Sobrien	if (xm->sibling == NULL)
35159243Sobrien	    return (0);
352167465Smp	inptr = &xm->sibling;
35359243Sobrien	ptr = xm->sibling;
35459243Sobrien    }
35559243Sobrien
35659243Sobrien    str->buf++;
35759243Sobrien    str->len--;
35859243Sobrien
35959243Sobrien    if (str->len == 0) {
36059243Sobrien	/* we're there */
361167465Smp	*inptr = ptr->sibling;
36259243Sobrien	ptr->sibling = NULL;
36359243Sobrien	PutFreeNode(ptr);
36459243Sobrien	return (1);
36559243Sobrien    }
36659243Sobrien    else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
36759243Sobrien	if (ptr->next != NULL)
36859243Sobrien	    return (0);
369167465Smp	*inptr = ptr->sibling;
37059243Sobrien	ptr->sibling = NULL;
37159243Sobrien	PutFreeNode(ptr);
37259243Sobrien	return (1);
37359243Sobrien    }
37459243Sobrien    else {
37559243Sobrien	return (0);
37659243Sobrien    }
37759243Sobrien}
37859243Sobrien
37959243Sobrien/* PutFreeNode():
38059243Sobrien *	Puts a tree of nodes onto free list using free(3).
38159243Sobrien */
38259243Sobrienstatic void
383167465SmpPutFreeNode(XmapNode *ptr)
38459243Sobrien{
38559243Sobrien    if (ptr == NULL)
38659243Sobrien	return;
38759243Sobrien
38859243Sobrien    if (ptr->next != NULL) {
38959243Sobrien	PutFreeNode(ptr->next);
39059243Sobrien	ptr->next = NULL;
39159243Sobrien    }
39259243Sobrien
39359243Sobrien    PutFreeNode(ptr->sibling);
39459243Sobrien
39559243Sobrien    switch (ptr->type) {
39659243Sobrien    case XK_CMD:
39759243Sobrien    case XK_NOD:
39859243Sobrien	break;
39959243Sobrien    case XK_EXE:
40059243Sobrien    case XK_STR:
401167465Smp	xfree(ptr->val.str.buf);
40259243Sobrien	break;
40359243Sobrien    default:
40459243Sobrien	abort();
40559243Sobrien	break;
40659243Sobrien    }
407167465Smp    xfree(ptr);
40859243Sobrien}
40959243Sobrien
41059243Sobrien
41159243Sobrien/* GetFreeNode():
41259243Sobrien *	Returns pointer to an XmapNode for ch.
41359243Sobrien */
41459243Sobrienstatic XmapNode *
415167465SmpGetFreeNode(CStr *ch)
41659243Sobrien{
41759243Sobrien    XmapNode *ptr;
41859243Sobrien
419167465Smp    ptr = xmalloc(sizeof(XmapNode));
42059243Sobrien    ptr->ch = ch->buf[0];
42159243Sobrien    ptr->type = XK_NOD;
42259243Sobrien    ptr->val.str.buf = NULL;
42359243Sobrien    ptr->val.str.len = 0;
42459243Sobrien    ptr->next = NULL;
42559243Sobrien    ptr->sibling = NULL;
42659243Sobrien    return (ptr);
42759243Sobrien}
42859243Sobrien
42959243Sobrien
43059243Sobrien/* PrintXKey():
43159243Sobrien *	Print the binding associated with Xkey key.
43259243Sobrien *	Print entire Xmap if null
43359243Sobrien */
43459243Sobrienvoid
435167465SmpPrintXkey(const CStr *key)
43659243Sobrien{
437167465Smp    struct Strbuf buf = Strbuf_INIT;
43859243Sobrien    CStr cs;
43959243Sobrien
44059243Sobrien    if (key) {
44159243Sobrien	cs.buf = key->buf;
44259243Sobrien	cs.len = key->len;
44359243Sobrien    }
44459243Sobrien    else {
44559243Sobrien	cs.buf = STRNULL;
44659243Sobrien	cs.len = 0;
44759243Sobrien    }
44859243Sobrien    /* do nothing if Xmap is empty and null key specified */
44959243Sobrien    if (Xmap == NULL && cs.len == 0)
45059243Sobrien	return;
45159243Sobrien
452167465Smp    Strbuf_append1(&buf, '"');
453167465Smp    cleanup_push(&buf, Strbuf_cleanup);
454167465Smp    if (Lookup(&buf, &cs, Xmap) <= -1)
45559243Sobrien	/* key is not bound */
45659243Sobrien	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
457167465Smp    cleanup_until(&buf);
45859243Sobrien}
45959243Sobrien
46059243Sobrien/* Lookup():
46159243Sobrien *	look for the string starting at node ptr.
46259243Sobrien *	Print if last node
46359243Sobrien */
46459243Sobrienstatic int
465167465SmpLookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
46659243Sobrien{
46759243Sobrien    if (ptr == NULL)
46859243Sobrien	return (-1);		/* cannot have null ptr */
46959243Sobrien
47059243Sobrien    if (str->len == 0) {
47159243Sobrien	/* no more chars in string.  Enumerate from here. */
472167465Smp	Enumerate(buf, ptr);
47359243Sobrien	return (0);
47459243Sobrien    }
47559243Sobrien    else {
476167465Smp	/* If match put this char into buf.  Recurse */
47759243Sobrien	if (ptr->ch == *(str->buf)) {
47859243Sobrien	    /* match found */
479167465Smp	    unparsech(buf, ptr->ch);
48059243Sobrien	    if (ptr->next != NULL) {
48159243Sobrien		/* not yet at leaf */
48259243Sobrien		CStr tstr;
48359243Sobrien		tstr.buf = str->buf + 1;
48459243Sobrien		tstr.len = str->len - 1;
485167465Smp		return (Lookup(buf, &tstr, ptr->next));
48659243Sobrien	    }
48759243Sobrien	    else {
48859243Sobrien		/* next node is null so key should be complete */
48959243Sobrien		if (str->len == 1) {
490167465Smp		    Strbuf_append1(buf, '"');
491167465Smp		    Strbuf_terminate(buf);
492167465Smp		    printOne(buf->s, &ptr->val, ptr->type);
49359243Sobrien		    return (0);
49459243Sobrien		}
49559243Sobrien		else
49659243Sobrien		    return (-1);/* mismatch -- string still has chars */
49759243Sobrien	    }
49859243Sobrien	}
49959243Sobrien	else {
50059243Sobrien	    /* no match found try sibling */
50159243Sobrien	    if (ptr->sibling)
502167465Smp		return (Lookup(buf, str, ptr->sibling));
50359243Sobrien	    else
50459243Sobrien		return (-1);
50559243Sobrien	}
50659243Sobrien    }
50759243Sobrien}
50859243Sobrien
509167465Smpstatic void
510167465SmpEnumerate(struct Strbuf *buf, const XmapNode *ptr)
51159243Sobrien{
512167465Smp    size_t old_len;
51359243Sobrien
51459243Sobrien    if (ptr == NULL) {
51559243Sobrien#ifdef DEBUG_EDIT
51659243Sobrien	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
51759243Sobrien#endif
518167465Smp	return;
51959243Sobrien    }
52059243Sobrien
521167465Smp    old_len = buf->len;
522167465Smp    unparsech(buf, ptr->ch); /* put this char at end of string */
52359243Sobrien    if (ptr->next == NULL) {
52459243Sobrien	/* print this Xkey and function */
525167465Smp	Strbuf_append1(buf, '"');
526167465Smp	Strbuf_terminate(buf);
527167465Smp	printOne(buf->s, &ptr->val, ptr->type);
52859243Sobrien    }
52959243Sobrien    else
530167465Smp	Enumerate(buf, ptr->next);
53159243Sobrien
53259243Sobrien    /* go to sibling if there is one */
533167465Smp    if (ptr->sibling) {
534167465Smp	buf->len = old_len;
535167465Smp	Enumerate(buf, ptr->sibling);
536167465Smp    }
53759243Sobrien}
53859243Sobrien
53959243Sobrien
54059243Sobrien/* PrintOne():
54159243Sobrien *	Print the specified key and its associated
54259243Sobrien *	function specified by val
54359243Sobrien */
544167465Smpvoid
545167465SmpprintOne(const Char *key, const XmapVal *val, int ntype)
54659243Sobrien{
54759243Sobrien    struct KeyFuncs *fp;
548145479Smp    static const char *fmt = "%s\n";
54959243Sobrien
550167465Smp    xprintf("%-15S-> ", key);
55159243Sobrien    if (val != NULL)
55259243Sobrien	switch (ntype) {
55359243Sobrien	case XK_STR:
554167465Smp	case XK_EXE: {
555167465Smp	    unsigned char *p;
556167465Smp
557167465Smp	    p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
558167465Smp	    cleanup_push(p, xfree);
559167465Smp	    xprintf(fmt, p);
560167465Smp	    cleanup_until(p);
56159243Sobrien	    break;
562167465Smp	}
56359243Sobrien	case XK_CMD:
56459243Sobrien	    for (fp = FuncNames; fp->name; fp++)
56559243Sobrien		if (val->cmd == fp->func)
56659243Sobrien		    xprintf(fmt, fp->name);
56759243Sobrien		break;
56859243Sobrien	default:
56959243Sobrien	    abort();
57059243Sobrien	    break;
57159243Sobrien	}
57259243Sobrien    else
573167465Smp	xprintf(fmt, CGETS(9, 7, "no input"));
57459243Sobrien}
57559243Sobrien
576167465Smpstatic void
577167465Smpunparsech(struct Strbuf *buf, Char ch)
57859243Sobrien{
57959243Sobrien    if (ch == 0) {
580167465Smp	Strbuf_append1(buf, '^');
581167465Smp	Strbuf_append1(buf, '@');
58259243Sobrien    }
583167465Smp    else if (Iscntrl(ch)) {
584167465Smp	Strbuf_append1(buf, '^');
585167465Smp	if (ch == CTL_ESC('\177'))
586167465Smp	    Strbuf_append1(buf, '?');
587167465Smp	else
58869408Sache#ifdef IS_ASCII
589167465Smp	    Strbuf_append1(buf, ch | 0100);
59069408Sache#else
591167465Smp	    Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
59269408Sache#endif
59359243Sobrien    }
594167465Smp    else if (ch == '^') {
595167465Smp	Strbuf_append1(buf, '\\');
596167465Smp	Strbuf_append1(buf, '^');
597167465Smp    } else if (ch == '\\') {
598167465Smp	Strbuf_append1(buf, '\\');
599167465Smp	Strbuf_append1(buf, '\\');
600167465Smp    } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
601167465Smp	Strbuf_append1(buf, ch);
60259243Sobrien    }
60359243Sobrien    else {
604167465Smp	Strbuf_append1(buf, '\\');
605167465Smp	Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
606167465Smp	Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
607167465Smp	Strbuf_append1(buf, (ch & 7) + '0');
60859243Sobrien    }
60959243Sobrien}
61059243Sobrien
611145479SmpeChar
612167465Smpparseescape(const Char **ptr)
61359243Sobrien{
61459243Sobrien    const Char *p;
61559243Sobrien    Char c;
61659243Sobrien
61759243Sobrien    p = *ptr;
61859243Sobrien
61959243Sobrien    if ((p[1] & CHAR) == 0) {
620167465Smp	xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
621145479Smp	return CHAR_ERR;
62259243Sobrien    }
62359243Sobrien    if ((*p & CHAR) == '\\') {
62459243Sobrien	p++;
62559243Sobrien	switch (*p & CHAR) {
62659243Sobrien	case 'a':
62759243Sobrien	    c = CTL_ESC('\007');         /* Bell */
62859243Sobrien	    break;
62959243Sobrien	case 'b':
63059243Sobrien	    c = CTL_ESC('\010');         /* Backspace */
63159243Sobrien	    break;
63259243Sobrien	case 'e':
63359243Sobrien	    c = CTL_ESC('\033');         /* Escape */
63459243Sobrien	    break;
63559243Sobrien	case 'f':
63659243Sobrien	    c = CTL_ESC('\014');         /* Form Feed */
63759243Sobrien	    break;
63859243Sobrien	case 'n':
63959243Sobrien	    c = CTL_ESC('\012');         /* New Line */
64059243Sobrien	    break;
64159243Sobrien	case 'r':
64259243Sobrien	    c = CTL_ESC('\015');         /* Carriage Return */
64359243Sobrien	    break;
64459243Sobrien	case 't':
64559243Sobrien	    c = CTL_ESC('\011');         /* Horizontal Tab */
64659243Sobrien	    break;
64759243Sobrien	case 'v':
64859243Sobrien	    c = CTL_ESC('\013');         /* Vertical Tab */
64959243Sobrien	    break;
65083098Smp	case '\\':
65183098Smp	    c = '\\';
65283098Smp	    break;
65359243Sobrien	case '0':
65459243Sobrien	case '1':
65559243Sobrien	case '2':
65659243Sobrien	case '3':
65759243Sobrien	case '4':
65859243Sobrien	case '5':
65959243Sobrien	case '6':
66059243Sobrien	case '7':
66159243Sobrien	    {
662145479Smp		int cnt, val;
663145479Smp		Char ch;
66459243Sobrien
66559243Sobrien		for (cnt = 0, val = 0; cnt < 3; cnt++) {
66659243Sobrien		    ch = *p++ & CHAR;
66759243Sobrien		    if (ch < '0' || ch > '7') {
66859243Sobrien			p--;
66959243Sobrien			break;
67059243Sobrien		    }
67159243Sobrien		    val = (val << 3) | (ch - '0');
67259243Sobrien		}
673167465Smp		if ((val & ~0xff) != 0) {
674195609Smp		    xprintf("%s", CGETS(9, 9,
67559243Sobrien			    "Octal constant does not fit in a char.\n"));
67659243Sobrien		    return 0;
67759243Sobrien		}
67869408Sache#ifndef IS_ASCII
67959243Sobrien		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
68059243Sobrien		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
68159243Sobrien			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
68259243Sobrien#endif
68359243Sobrien		c = (Char) val;
68459243Sobrien		--p;
68559243Sobrien	    }
68659243Sobrien	    break;
68759243Sobrien	default:
68859243Sobrien	    c = *p;
68959243Sobrien	    break;
69059243Sobrien	}
69159243Sobrien    }
69259243Sobrien    else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
69359243Sobrien				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
69459243Sobrien	p++;
69569408Sache#ifdef IS_ASCII
69659243Sobrien	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
69769408Sache#else
69859243Sobrien	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
69959243Sobrien	if (adrof(STRwarnebcdic))
70059243Sobrien	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
70159243Sobrien		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
70269408Sache#endif
70359243Sobrien    }
70459243Sobrien    else
70559243Sobrien	c = *p;
70659243Sobrien    *ptr = p;
70759243Sobrien    return (c);
70859243Sobrien}
70959243Sobrien
71059243Sobrien
71159243Sobrienunsigned char *
712167465Smpunparsestring(const CStr *str, const Char *sep)
71359243Sobrien{
714167465Smp    unsigned char *buf, *b;
71559243Sobrien    Char   p;
71659243Sobrien    int l;
71759243Sobrien
718167465Smp    /* Worst-case is "\uuu" or result of wctomb() for each char from str */
719167465Smp    buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
72059243Sobrien    b = buf;
72159243Sobrien    if (sep[0])
72269408Sache#ifndef WINNT_NATIVE
72359243Sobrien	*b++ = sep[0];
72469408Sache#else /* WINNT_NATIVE */
72559243Sobrien	*b++ = CHAR & sep[0];
72669408Sache#endif /* !WINNT_NATIVE */
72759243Sobrien
72859243Sobrien    for (l = 0; l < str->len; l++) {
72959243Sobrien	p = str->buf[l];
73059243Sobrien	if (Iscntrl(p)) {
73159243Sobrien	    *b++ = '^';
73259243Sobrien	    if (p == CTL_ESC('\177'))
73359243Sobrien		*b++ = '?';
73459243Sobrien	    else
735167465Smp#ifdef IS_ASCII
73659243Sobrien		*b++ = (unsigned char) (p | 0100);
73769408Sache#else
738167465Smp		*b++ = _toebcdic[_toascii[p]|0100];
73969408Sache#endif
74059243Sobrien	}
74159243Sobrien	else if (p == '^' || p == '\\') {
74259243Sobrien	    *b++ = '\\';
74359243Sobrien	    *b++ = (unsigned char) p;
74459243Sobrien	}
745145479Smp	else if (p == ' ' || (Isprint(p) && !Isspace(p)))
746316958Sdchagin	    b += one_wctomb((char *)b, p);
74759243Sobrien	else {
74859243Sobrien	    *b++ = '\\';
74959243Sobrien	    *b++ = ((p >> 6) & 7) + '0';
75059243Sobrien	    *b++ = ((p >> 3) & 7) + '0';
75159243Sobrien	    *b++ = (p & 7) + '0';
75259243Sobrien	}
75359243Sobrien    }
75459243Sobrien    if (sep[0] && sep[1])
75569408Sache#ifndef WINNT_NATIVE
75659243Sobrien	*b++ = sep[1];
75769408Sache#else /* WINNT_NATIVE */
75859243Sobrien	*b++ = CHAR & sep[1];
75969408Sache#endif /* !WINNT_NATIVE */
76059243Sobrien    *b++ = 0;
76159243Sobrien    return buf;			/* should check for overflow */
76259243Sobrien}
763