159243Sobrien/*
259243Sobrien * ed.xmap.c: This module contains the procedures for maintaining
359243Sobrien *	      the extended-key map.
459243Sobrien *
559243Sobrien * 	      An extended-key (Xkey) is a sequence of keystrokes
659243Sobrien *	      introduced with an sequence introducer and consisting
759243Sobrien *	      of an arbitrary number of characters.  This module maintains
859243Sobrien *	      a map (the Xmap) to convert these extended-key sequences
959243Sobrien * 	      into input strings (XK_STR), editor functions (XK_CMD), or
1059243Sobrien *	      unix commands (XK_EXE). It contains the
1159243Sobrien *	      following externally visible functions.
1259243Sobrien *
1359243Sobrien *		int GetXkey(ch,val);
1459243Sobrien *		CStr *ch;
1559243Sobrien *		XmapVal *val;
1659243Sobrien *
1759243Sobrien *	      Looks up *ch in map and then reads characters until a
1859243Sobrien *	      complete match is found or a mismatch occurs. Returns the
1959243Sobrien *	      type of the match found (XK_STR, XK_CMD, or XK_EXE).
2059243Sobrien *	      Returns NULL in val.str and XK_STR for no match.
2159243Sobrien *	      The last character read is returned in *ch.
2259243Sobrien *
2359243Sobrien *		void AddXkey(Xkey, val, ntype);
2459243Sobrien *		CStr *Xkey;
2559243Sobrien *		XmapVal *val;
2659243Sobrien *		int ntype;
2759243Sobrien *
2859243Sobrien *	      Adds Xkey to the Xmap and associates the value in val with it.
2959243Sobrien *	      If Xkey is already is in Xmap, the new code is applied to the
3059243Sobrien *	      existing Xkey. Ntype specifies if code is a command, an
3159243Sobrien *	      out string or a unix command.
3259243Sobrien *
3359243Sobrien *	        int DeleteXkey(Xkey);
3459243Sobrien *	        CStr *Xkey;
3559243Sobrien *
3659243Sobrien *	      Delete the Xkey and all longer Xkeys staring with Xkey, if
3759243Sobrien *	      they exists.
3859243Sobrien *
3959243Sobrien *	      Warning:
4059243Sobrien *		If Xkey is a substring of some other Xkeys, then the longer
4159243Sobrien *		Xkeys are lost!!  That is, if the Xkeys "abcd" and "abcef"
4259243Sobrien *		are in Xmap, adding the key "abc" will cause the first two
4359243Sobrien *		definitions to be lost.
4459243Sobrien *
4559243Sobrien *		void ResetXmap();
4659243Sobrien *
4759243Sobrien *	      Removes all entries from Xmap and resets the defaults.
4859243Sobrien *
4959243Sobrien *		void PrintXkey(Xkey);
5059243Sobrien *		CStr *Xkey;
5159243Sobrien *
5259243Sobrien *	      Prints all extended keys prefixed by Xkey and their associated
5359243Sobrien *	      commands.
5459243Sobrien *
5559243Sobrien *	      Restrictions:
5659243Sobrien *	      -------------
5759243Sobrien *	        1) It is not possible to have one Xkey that is a
5859243Sobrien *		   substring of another.
5959243Sobrien */
6059243Sobrien/*-
6159243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
6259243Sobrien * All rights reserved.
6359243Sobrien *
6459243Sobrien * Redistribution and use in source and binary forms, with or without
6559243Sobrien * modification, are permitted provided that the following conditions
6659243Sobrien * are met:
6759243Sobrien * 1. Redistributions of source code must retain the above copyright
6859243Sobrien *    notice, this list of conditions and the following disclaimer.
6959243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
7059243Sobrien *    notice, this list of conditions and the following disclaimer in the
7159243Sobrien *    documentation and/or other materials provided with the distribution.
72100616Smp * 3. Neither the name of the University nor the names of its contributors
7359243Sobrien *    may be used to endorse or promote products derived from this software
7459243Sobrien *    without specific prior written permission.
7559243Sobrien *
7659243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
7759243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
7859243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
7959243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
8059243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
8159243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
8259243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
8359243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
8459243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
8559243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
8659243Sobrien * SUCH DAMAGE.
8759243Sobrien */
8859243Sobrien#include "sh.h"
8959243Sobrien#include "ed.h"
9059243Sobrien#include "ed.defns.h"
9159243Sobrien
9259243Sobrien#ifndef NULL
9359243Sobrien#define NULL 0
9459243Sobrien#endif
9559243Sobrien
9659243Sobrien/* Internal Data types and declarations */
9759243Sobrien
9859243Sobrien/* The Nodes of the Xmap.  The Xmap is a linked list of these node
9959243Sobrien * elements
10059243Sobrien */
10159243Sobrientypedef struct Xmapnode {
10259243Sobrien    Char    ch;			/* single character of Xkey */
10359243Sobrien    int     type;
10459243Sobrien    XmapVal val; 		/* command code or pointer to string, if this
10559243Sobrien				 * is a leaf */
10659243Sobrien    struct Xmapnode *next;	/* ptr to next char of this Xkey */
10759243Sobrien    struct Xmapnode *sibling;	/* ptr to another Xkey with same prefix */
10859243Sobrien} XmapNode;
10959243Sobrien
11059243Sobrienstatic XmapNode *Xmap = NULL;	/* the current Xmap */
11159243Sobrien
11259243Sobrien
11359243Sobrien/* Some declarations of procedures */
114167465Smpstatic	int       TraverseMap	(XmapNode *, CStr *, XmapVal *);
115167465Smpstatic	int       TryNode	(XmapNode *, CStr *, XmapVal *, int);
116167465Smpstatic	XmapNode *GetFreeNode	(CStr *);
117167465Smpstatic	void	  PutFreeNode	(XmapNode *);
118167465Smpstatic	int	  TryDeleteNode	(XmapNode **, CStr *);
119167465Smpstatic	int	  Lookup	(struct Strbuf *, const CStr *,
120167465Smp				 const XmapNode *);
121167465Smpstatic	void	  Enumerate	(struct Strbuf *, const XmapNode *);
122167465Smpstatic	void	  unparsech	(struct Strbuf *, Char);
12359243Sobrien
12459243Sobrien
12559243SobrienXmapVal *
126167465SmpXmapCmd(int cmd)
12759243Sobrien{
12859243Sobrien    static XmapVal xm;
12959243Sobrien    xm.cmd = (KEYCMD) cmd;
13059243Sobrien    return &xm;
13159243Sobrien}
13259243Sobrien
13359243SobrienXmapVal *
134167465SmpXmapStr(CStr *str)
13559243Sobrien{
13659243Sobrien    static XmapVal xm;
13759243Sobrien    xm.str.len = str->len;
13859243Sobrien    xm.str.buf = str->buf;
13959243Sobrien    return &xm;
14059243Sobrien}
14159243Sobrien
14259243Sobrien/* ResetXmap():
14359243Sobrien *	Takes all nodes on Xmap and puts them on free list.  Then
14459243Sobrien *	initializes Xmap with arrow keys
14559243Sobrien */
14659243Sobrienvoid
147167465SmpResetXmap(void)
14859243Sobrien{
14959243Sobrien    PutFreeNode(Xmap);
15059243Sobrien    Xmap = NULL;
15159243Sobrien
15259243Sobrien    DefaultArrowKeys();
15359243Sobrien    return;
15459243Sobrien}
15559243Sobrien
15659243Sobrien
15759243Sobrien/* GetXkey():
15859243Sobrien *	Calls the recursive function with entry point Xmap
15959243Sobrien */
16059243Sobrienint
161167465SmpGetXkey(CStr *ch, XmapVal *val)
16259243Sobrien{
16359243Sobrien    return (TraverseMap(Xmap, ch, val));
16459243Sobrien}
16559243Sobrien
16659243Sobrien/* TraverseMap():
16759243Sobrien *	recursively traverses node in tree until match or mismatch is
16859243Sobrien * 	found.  May read in more characters.
16959243Sobrien */
17059243Sobrienstatic int
171167465SmpTraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
17259243Sobrien{
17359243Sobrien    Char    tch;
17459243Sobrien
17559243Sobrien    if (ptr->ch == *(ch->buf)) {
17659243Sobrien	/* match found */
17759243Sobrien	if (ptr->next) {
17859243Sobrien	    /* Xkey not complete so get next char */
17959243Sobrien	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
18059243Sobrien		val->cmd = F_SEND_EOF;
18159243Sobrien		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
18259243Sobrien	    }
18359243Sobrien	    *(ch->buf) = tch;
18459243Sobrien	    return (TraverseMap(ptr->next, ch, val));
18559243Sobrien	}
18659243Sobrien	else {
18759243Sobrien	    *val = ptr->val;
18859243Sobrien	    if (ptr->type != XK_CMD)
18959243Sobrien		*(ch->buf) = '\0';
19059243Sobrien	    return ptr->type;
19159243Sobrien	}
19259243Sobrien    }
19359243Sobrien    else {
19459243Sobrien	/* no match found here */
19559243Sobrien	if (ptr->sibling) {
19659243Sobrien	    /* try next sibling */
19759243Sobrien	    return (TraverseMap(ptr->sibling, ch, val));
19859243Sobrien	}
19959243Sobrien	else {
20059243Sobrien	    /* no next sibling -- mismatch */
20159243Sobrien	    val->str.buf = NULL;
20259243Sobrien	    val->str.len = 0;
20359243Sobrien	    return XK_STR;
20459243Sobrien	}
20559243Sobrien    }
20659243Sobrien}
20759243Sobrien
20859243Sobrienvoid
209167465SmpAddXkey(const CStr *Xkey, XmapVal *val, int ntype)
21059243Sobrien{
21159243Sobrien    CStr cs;
21259243Sobrien    cs.buf = Xkey->buf;
21359243Sobrien    cs.len = Xkey->len;
21459243Sobrien    if (Xkey->len == 0) {
215195609Smp	xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
21659243Sobrien	return;
21759243Sobrien    }
21859243Sobrien
21959243Sobrien    if (ntype == XK_CMD && val->cmd == F_XKEY) {
220195609Smp	xprintf("%s",
221195609Smp	    CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
22259243Sobrien	return;
22359243Sobrien    }
22459243Sobrien
22559243Sobrien    if (Xmap == NULL)
22659243Sobrien	/* tree is initially empty.  Set up new node to match Xkey[0] */
22759243Sobrien	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
22859243Sobrien
22959243Sobrien    /* Now recurse through Xmap */
23059243Sobrien    (void) TryNode(Xmap, &cs, val, ntype);
23159243Sobrien    return;
23259243Sobrien}
23359243Sobrien
23459243Sobrienstatic int
235167465SmpTryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
23659243Sobrien{
23759243Sobrien    /*
23859243Sobrien     * Find a node that matches *string or allocate a new one
23959243Sobrien     */
24059243Sobrien    if (ptr->ch != *(str->buf)) {
24159243Sobrien	XmapNode *xm;
24259243Sobrien
24359243Sobrien	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
24459243Sobrien	    if (xm->sibling->ch == *(str->buf))
24559243Sobrien		break;
24659243Sobrien	if (xm->sibling == NULL)
24759243Sobrien	    xm->sibling = GetFreeNode(str);	/* setup new node */
24859243Sobrien	ptr = xm->sibling;
24959243Sobrien    }
25059243Sobrien
25159243Sobrien    str->buf++;
25259243Sobrien    str->len--;
25359243Sobrien    if (str->len == 0) {
254167465Smp	size_t len;
255167465Smp
25659243Sobrien	/* we're there */
25759243Sobrien	if (ptr->next != NULL) {
25859243Sobrien	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
25959243Sobrien	    ptr->next = NULL;
26059243Sobrien	}
26159243Sobrien
26259243Sobrien	switch (ptr->type) {
26359243Sobrien	case XK_STR:
26459243Sobrien	case XK_EXE:
265167465Smp	    xfree(ptr->val.str.buf);
26659243Sobrien	    ptr->val.str.len = 0;
26759243Sobrien	    break;
26859243Sobrien	case XK_NOD:
26959243Sobrien	case XK_CMD:
27059243Sobrien	    break;
27159243Sobrien	default:
27259243Sobrien	    abort();
27359243Sobrien	    break;
27459243Sobrien	}
27559243Sobrien
27659243Sobrien	switch (ptr->type = ntype) {
27759243Sobrien	case XK_CMD:
27859243Sobrien	    ptr->val = *val;
27959243Sobrien	    break;
28059243Sobrien	case XK_STR:
28159243Sobrien	case XK_EXE:
28259243Sobrien	    ptr->val.str.len = val->str.len;
283167465Smp	    len = (val->str.len + 1) * sizeof(*ptr->val.str.buf);
284167465Smp	    ptr->val.str.buf = xmalloc(len);
285167465Smp	    (void) memcpy(ptr->val.str.buf, val->str.buf, len);
28659243Sobrien	    break;
28759243Sobrien	default:
28859243Sobrien	    abort();
28959243Sobrien	    break;
29059243Sobrien	}
29159243Sobrien    }
29259243Sobrien    else {
29359243Sobrien	/* still more chars to go */
29459243Sobrien	if (ptr->next == NULL)
29559243Sobrien	    ptr->next = GetFreeNode(str);	/* setup new node */
29659243Sobrien	(void) TryNode(ptr->next, str, val, ntype);
29759243Sobrien    }
29859243Sobrien    return (0);
29959243Sobrien}
30059243Sobrien
30159243Sobrienvoid
302167465SmpClearXkey(KEYCMD *map, const CStr *in)
30359243Sobrien{
30459243Sobrien    unsigned char c = (unsigned char) *(in->buf);
30559243Sobrien    if ((map[c] == F_XKEY) &&
30659243Sobrien	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
30759243Sobrien	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
30859243Sobrien	(void) DeleteXkey(in);
30959243Sobrien}
31059243Sobrien
31159243Sobrienint
312167465SmpDeleteXkey(const CStr *Xkey)
31359243Sobrien{
314167465Smp    CStr s;
315167465Smp
316167465Smp    s = *Xkey;
317167465Smp    if (s.len == 0) {
318195609Smp	xprintf("%s",
319195609Smp	        CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
32059243Sobrien	return (-1);
32159243Sobrien    }
32259243Sobrien
32359243Sobrien    if (Xmap == NULL)
32459243Sobrien	return (0);
32559243Sobrien
326167465Smp    (void) TryDeleteNode(&Xmap, &s);
32759243Sobrien    return (0);
32859243Sobrien}
32959243Sobrien
330167465Smp/* Destroys str */
33159243Sobrienstatic int
332167465SmpTryDeleteNode(XmapNode **inptr, CStr *str)
33359243Sobrien{
33459243Sobrien    XmapNode *ptr;
33559243Sobrien
33659243Sobrien    ptr = *inptr;
33759243Sobrien    /*
33859243Sobrien     * Find a node that matches *string or allocate a new one
33959243Sobrien     */
34059243Sobrien    if (ptr->ch != *(str->buf)) {
34159243Sobrien	XmapNode *xm;
34259243Sobrien
34359243Sobrien	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
34459243Sobrien	    if (xm->sibling->ch == *(str->buf))
34559243Sobrien		break;
34659243Sobrien	if (xm->sibling == NULL)
34759243Sobrien	    return (0);
348167465Smp	inptr = &xm->sibling;
34959243Sobrien	ptr = xm->sibling;
35059243Sobrien    }
35159243Sobrien
35259243Sobrien    str->buf++;
35359243Sobrien    str->len--;
35459243Sobrien
35559243Sobrien    if (str->len == 0) {
35659243Sobrien	/* we're there */
357167465Smp	*inptr = ptr->sibling;
35859243Sobrien	ptr->sibling = NULL;
35959243Sobrien	PutFreeNode(ptr);
36059243Sobrien	return (1);
36159243Sobrien    }
36259243Sobrien    else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
36359243Sobrien	if (ptr->next != NULL)
36459243Sobrien	    return (0);
365167465Smp	*inptr = ptr->sibling;
36659243Sobrien	ptr->sibling = NULL;
36759243Sobrien	PutFreeNode(ptr);
36859243Sobrien	return (1);
36959243Sobrien    }
37059243Sobrien    else {
37159243Sobrien	return (0);
37259243Sobrien    }
37359243Sobrien}
37459243Sobrien
37559243Sobrien/* PutFreeNode():
37659243Sobrien *	Puts a tree of nodes onto free list using free(3).
37759243Sobrien */
37859243Sobrienstatic void
379167465SmpPutFreeNode(XmapNode *ptr)
38059243Sobrien{
38159243Sobrien    if (ptr == NULL)
38259243Sobrien	return;
38359243Sobrien
38459243Sobrien    if (ptr->next != NULL) {
38559243Sobrien	PutFreeNode(ptr->next);
38659243Sobrien	ptr->next = NULL;
38759243Sobrien    }
38859243Sobrien
38959243Sobrien    PutFreeNode(ptr->sibling);
39059243Sobrien
39159243Sobrien    switch (ptr->type) {
39259243Sobrien    case XK_CMD:
39359243Sobrien    case XK_NOD:
39459243Sobrien	break;
39559243Sobrien    case XK_EXE:
39659243Sobrien    case XK_STR:
397167465Smp	xfree(ptr->val.str.buf);
39859243Sobrien	break;
39959243Sobrien    default:
40059243Sobrien	abort();
40159243Sobrien	break;
40259243Sobrien    }
403167465Smp    xfree(ptr);
40459243Sobrien}
40559243Sobrien
40659243Sobrien
40759243Sobrien/* GetFreeNode():
40859243Sobrien *	Returns pointer to an XmapNode for ch.
40959243Sobrien */
41059243Sobrienstatic XmapNode *
411167465SmpGetFreeNode(CStr *ch)
41259243Sobrien{
41359243Sobrien    XmapNode *ptr;
41459243Sobrien
415167465Smp    ptr = xmalloc(sizeof(XmapNode));
41659243Sobrien    ptr->ch = ch->buf[0];
41759243Sobrien    ptr->type = XK_NOD;
41859243Sobrien    ptr->val.str.buf = NULL;
41959243Sobrien    ptr->val.str.len = 0;
42059243Sobrien    ptr->next = NULL;
42159243Sobrien    ptr->sibling = NULL;
42259243Sobrien    return (ptr);
42359243Sobrien}
42459243Sobrien
42559243Sobrien
42659243Sobrien/* PrintXKey():
42759243Sobrien *	Print the binding associated with Xkey key.
42859243Sobrien *	Print entire Xmap if null
42959243Sobrien */
43059243Sobrienvoid
431167465SmpPrintXkey(const CStr *key)
43259243Sobrien{
433167465Smp    struct Strbuf buf = Strbuf_INIT;
43459243Sobrien    CStr cs;
43559243Sobrien
43659243Sobrien    if (key) {
43759243Sobrien	cs.buf = key->buf;
43859243Sobrien	cs.len = key->len;
43959243Sobrien    }
44059243Sobrien    else {
44159243Sobrien	cs.buf = STRNULL;
44259243Sobrien	cs.len = 0;
44359243Sobrien    }
44459243Sobrien    /* do nothing if Xmap is empty and null key specified */
44559243Sobrien    if (Xmap == NULL && cs.len == 0)
44659243Sobrien	return;
44759243Sobrien
448167465Smp    Strbuf_append1(&buf, '"');
449167465Smp    cleanup_push(&buf, Strbuf_cleanup);
450167465Smp    if (Lookup(&buf, &cs, Xmap) <= -1)
45159243Sobrien	/* key is not bound */
45259243Sobrien	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
453167465Smp    cleanup_until(&buf);
45459243Sobrien}
45559243Sobrien
45659243Sobrien/* Lookup():
45759243Sobrien *	look for the string starting at node ptr.
45859243Sobrien *	Print if last node
45959243Sobrien */
46059243Sobrienstatic int
461167465SmpLookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
46259243Sobrien{
46359243Sobrien    if (ptr == NULL)
46459243Sobrien	return (-1);		/* cannot have null ptr */
46559243Sobrien
46659243Sobrien    if (str->len == 0) {
46759243Sobrien	/* no more chars in string.  Enumerate from here. */
468167465Smp	Enumerate(buf, ptr);
46959243Sobrien	return (0);
47059243Sobrien    }
47159243Sobrien    else {
472167465Smp	/* If match put this char into buf.  Recurse */
47359243Sobrien	if (ptr->ch == *(str->buf)) {
47459243Sobrien	    /* match found */
475167465Smp	    unparsech(buf, ptr->ch);
47659243Sobrien	    if (ptr->next != NULL) {
47759243Sobrien		/* not yet at leaf */
47859243Sobrien		CStr tstr;
47959243Sobrien		tstr.buf = str->buf + 1;
48059243Sobrien		tstr.len = str->len - 1;
481167465Smp		return (Lookup(buf, &tstr, ptr->next));
48259243Sobrien	    }
48359243Sobrien	    else {
48459243Sobrien		/* next node is null so key should be complete */
48559243Sobrien		if (str->len == 1) {
486167465Smp		    Strbuf_append1(buf, '"');
487167465Smp		    Strbuf_terminate(buf);
488167465Smp		    printOne(buf->s, &ptr->val, ptr->type);
48959243Sobrien		    return (0);
49059243Sobrien		}
49159243Sobrien		else
49259243Sobrien		    return (-1);/* mismatch -- string still has chars */
49359243Sobrien	    }
49459243Sobrien	}
49559243Sobrien	else {
49659243Sobrien	    /* no match found try sibling */
49759243Sobrien	    if (ptr->sibling)
498167465Smp		return (Lookup(buf, str, ptr->sibling));
49959243Sobrien	    else
50059243Sobrien		return (-1);
50159243Sobrien	}
50259243Sobrien    }
50359243Sobrien}
50459243Sobrien
505167465Smpstatic void
506167465SmpEnumerate(struct Strbuf *buf, const XmapNode *ptr)
50759243Sobrien{
508167465Smp    size_t old_len;
50959243Sobrien
51059243Sobrien    if (ptr == NULL) {
51159243Sobrien#ifdef DEBUG_EDIT
51259243Sobrien	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
51359243Sobrien#endif
514167465Smp	return;
51559243Sobrien    }
51659243Sobrien
517167465Smp    old_len = buf->len;
518167465Smp    unparsech(buf, ptr->ch); /* put this char at end of string */
51959243Sobrien    if (ptr->next == NULL) {
52059243Sobrien	/* print this Xkey and function */
521167465Smp	Strbuf_append1(buf, '"');
522167465Smp	Strbuf_terminate(buf);
523167465Smp	printOne(buf->s, &ptr->val, ptr->type);
52459243Sobrien    }
52559243Sobrien    else
526167465Smp	Enumerate(buf, ptr->next);
52759243Sobrien
52859243Sobrien    /* go to sibling if there is one */
529167465Smp    if (ptr->sibling) {
530167465Smp	buf->len = old_len;
531167465Smp	Enumerate(buf, ptr->sibling);
532167465Smp    }
53359243Sobrien}
53459243Sobrien
53559243Sobrien
53659243Sobrien/* PrintOne():
53759243Sobrien *	Print the specified key and its associated
53859243Sobrien *	function specified by val
53959243Sobrien */
540167465Smpvoid
541167465SmpprintOne(const Char *key, const XmapVal *val, int ntype)
54259243Sobrien{
54359243Sobrien    struct KeyFuncs *fp;
544145479Smp    static const char *fmt = "%s\n";
54559243Sobrien
546167465Smp    xprintf("%-15S-> ", key);
54759243Sobrien    if (val != NULL)
54859243Sobrien	switch (ntype) {
54959243Sobrien	case XK_STR:
550167465Smp	case XK_EXE: {
551167465Smp	    unsigned char *p;
552167465Smp
553167465Smp	    p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
554167465Smp	    cleanup_push(p, xfree);
555167465Smp	    xprintf(fmt, p);
556167465Smp	    cleanup_until(p);
55759243Sobrien	    break;
558167465Smp	}
55959243Sobrien	case XK_CMD:
56059243Sobrien	    for (fp = FuncNames; fp->name; fp++)
56159243Sobrien		if (val->cmd == fp->func)
56259243Sobrien		    xprintf(fmt, fp->name);
56359243Sobrien		break;
56459243Sobrien	default:
56559243Sobrien	    abort();
56659243Sobrien	    break;
56759243Sobrien	}
56859243Sobrien    else
569167465Smp	xprintf(fmt, CGETS(9, 7, "no input"));
57059243Sobrien}
57159243Sobrien
572167465Smpstatic void
573167465Smpunparsech(struct Strbuf *buf, Char ch)
57459243Sobrien{
57559243Sobrien    if (ch == 0) {
576167465Smp	Strbuf_append1(buf, '^');
577167465Smp	Strbuf_append1(buf, '@');
57859243Sobrien    }
579167465Smp    else if (Iscntrl(ch)) {
580167465Smp	Strbuf_append1(buf, '^');
581167465Smp	if (ch == CTL_ESC('\177'))
582167465Smp	    Strbuf_append1(buf, '?');
583167465Smp	else
58469408Sache#ifdef IS_ASCII
585167465Smp	    Strbuf_append1(buf, ch | 0100);
58669408Sache#else
587167465Smp	    Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
58869408Sache#endif
58959243Sobrien    }
590167465Smp    else if (ch == '^') {
591167465Smp	Strbuf_append1(buf, '\\');
592167465Smp	Strbuf_append1(buf, '^');
593167465Smp    } else if (ch == '\\') {
594167465Smp	Strbuf_append1(buf, '\\');
595167465Smp	Strbuf_append1(buf, '\\');
596167465Smp    } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
597167465Smp	Strbuf_append1(buf, ch);
59859243Sobrien    }
59959243Sobrien    else {
600167465Smp	Strbuf_append1(buf, '\\');
601167465Smp	Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
602167465Smp	Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
603167465Smp	Strbuf_append1(buf, (ch & 7) + '0');
60459243Sobrien    }
60559243Sobrien}
60659243Sobrien
607145479SmpeChar
608167465Smpparseescape(const Char **ptr)
60959243Sobrien{
61059243Sobrien    const Char *p;
61159243Sobrien    Char c;
61259243Sobrien
61359243Sobrien    p = *ptr;
61459243Sobrien
61559243Sobrien    if ((p[1] & CHAR) == 0) {
616167465Smp	xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
617145479Smp	return CHAR_ERR;
61859243Sobrien    }
61959243Sobrien    if ((*p & CHAR) == '\\') {
62059243Sobrien	p++;
62159243Sobrien	switch (*p & CHAR) {
62259243Sobrien	case 'a':
62359243Sobrien	    c = CTL_ESC('\007');         /* Bell */
62459243Sobrien	    break;
62559243Sobrien	case 'b':
62659243Sobrien	    c = CTL_ESC('\010');         /* Backspace */
62759243Sobrien	    break;
62859243Sobrien	case 'e':
62959243Sobrien	    c = CTL_ESC('\033');         /* Escape */
63059243Sobrien	    break;
63159243Sobrien	case 'f':
63259243Sobrien	    c = CTL_ESC('\014');         /* Form Feed */
63359243Sobrien	    break;
63459243Sobrien	case 'n':
63559243Sobrien	    c = CTL_ESC('\012');         /* New Line */
63659243Sobrien	    break;
63759243Sobrien	case 'r':
63859243Sobrien	    c = CTL_ESC('\015');         /* Carriage Return */
63959243Sobrien	    break;
64059243Sobrien	case 't':
64159243Sobrien	    c = CTL_ESC('\011');         /* Horizontal Tab */
64259243Sobrien	    break;
64359243Sobrien	case 'v':
64459243Sobrien	    c = CTL_ESC('\013');         /* Vertical Tab */
64559243Sobrien	    break;
64683098Smp	case '\\':
64783098Smp	    c = '\\';
64883098Smp	    break;
64959243Sobrien	case '0':
65059243Sobrien	case '1':
65159243Sobrien	case '2':
65259243Sobrien	case '3':
65359243Sobrien	case '4':
65459243Sobrien	case '5':
65559243Sobrien	case '6':
65659243Sobrien	case '7':
65759243Sobrien	    {
658145479Smp		int cnt, val;
659145479Smp		Char ch;
66059243Sobrien
66159243Sobrien		for (cnt = 0, val = 0; cnt < 3; cnt++) {
66259243Sobrien		    ch = *p++ & CHAR;
66359243Sobrien		    if (ch < '0' || ch > '7') {
66459243Sobrien			p--;
66559243Sobrien			break;
66659243Sobrien		    }
66759243Sobrien		    val = (val << 3) | (ch - '0');
66859243Sobrien		}
669167465Smp		if ((val & ~0xff) != 0) {
670195609Smp		    xprintf("%s", CGETS(9, 9,
67159243Sobrien			    "Octal constant does not fit in a char.\n"));
67259243Sobrien		    return 0;
67359243Sobrien		}
67469408Sache#ifndef IS_ASCII
67559243Sobrien		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
67659243Sobrien		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
67759243Sobrien			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
67859243Sobrien#endif
67959243Sobrien		c = (Char) val;
68059243Sobrien		--p;
68159243Sobrien	    }
68259243Sobrien	    break;
68359243Sobrien	default:
68459243Sobrien	    c = *p;
68559243Sobrien	    break;
68659243Sobrien	}
68759243Sobrien    }
68859243Sobrien    else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
68959243Sobrien				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
69059243Sobrien	p++;
69169408Sache#ifdef IS_ASCII
69259243Sobrien	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
69369408Sache#else
69459243Sobrien	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
69559243Sobrien	if (adrof(STRwarnebcdic))
69659243Sobrien	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
69759243Sobrien		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
69869408Sache#endif
69959243Sobrien    }
70059243Sobrien    else
70159243Sobrien	c = *p;
70259243Sobrien    *ptr = p;
70359243Sobrien    return (c);
70459243Sobrien}
70559243Sobrien
70659243Sobrien
70759243Sobrienunsigned char *
708167465Smpunparsestring(const CStr *str, const Char *sep)
70959243Sobrien{
710167465Smp    unsigned char *buf, *b;
71159243Sobrien    Char   p;
71259243Sobrien    int l;
71359243Sobrien
714167465Smp    /* Worst-case is "\uuu" or result of wctomb() for each char from str */
715167465Smp    buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
71659243Sobrien    b = buf;
71759243Sobrien    if (sep[0])
71869408Sache#ifndef WINNT_NATIVE
71959243Sobrien	*b++ = sep[0];
72069408Sache#else /* WINNT_NATIVE */
72159243Sobrien	*b++ = CHAR & sep[0];
72269408Sache#endif /* !WINNT_NATIVE */
72359243Sobrien
72459243Sobrien    for (l = 0; l < str->len; l++) {
72559243Sobrien	p = str->buf[l];
72659243Sobrien	if (Iscntrl(p)) {
72759243Sobrien	    *b++ = '^';
72859243Sobrien	    if (p == CTL_ESC('\177'))
72959243Sobrien		*b++ = '?';
73059243Sobrien	    else
731167465Smp#ifdef IS_ASCII
73259243Sobrien		*b++ = (unsigned char) (p | 0100);
73369408Sache#else
734167465Smp		*b++ = _toebcdic[_toascii[p]|0100];
73569408Sache#endif
73659243Sobrien	}
73759243Sobrien	else if (p == '^' || p == '\\') {
73859243Sobrien	    *b++ = '\\';
73959243Sobrien	    *b++ = (unsigned char) p;
74059243Sobrien	}
741145479Smp	else if (p == ' ' || (Isprint(p) && !Isspace(p)))
742316957Sdchagin	    b += one_wctomb((char *)b, p);
74359243Sobrien	else {
74459243Sobrien	    *b++ = '\\';
74559243Sobrien	    *b++ = ((p >> 6) & 7) + '0';
74659243Sobrien	    *b++ = ((p >> 3) & 7) + '0';
74759243Sobrien	    *b++ = (p & 7) + '0';
74859243Sobrien	}
74959243Sobrien    }
75059243Sobrien    if (sep[0] && sep[1])
75169408Sache#ifndef WINNT_NATIVE
75259243Sobrien	*b++ = sep[1];
75369408Sache#else /* WINNT_NATIVE */
75459243Sobrien	*b++ = CHAR & sep[1];
75569408Sache#endif /* !WINNT_NATIVE */
75659243Sobrien    *b++ = 0;
75759243Sobrien    return buf;			/* should check for overflow */
75859243Sobrien}
759