ed.xmap.c revision 267654
1290650Shselasky/* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $ */
2290650Shselasky/*
3290650Shselasky * ed.xmap.c: This module contains the procedures for maintaining
4290650Shselasky *	      the extended-key map.
5290650Shselasky *
6290650Shselasky * 	      An extended-key (Xkey) is a sequence of keystrokes
7290650Shselasky *	      introduced with an sequence introducer and consisting
8290650Shselasky *	      of an arbitrary number of characters.  This module maintains
9290650Shselasky *	      a map (the Xmap) to convert these extended-key sequences
10290650Shselasky * 	      into input strings (XK_STR), editor functions (XK_CMD), or
11290650Shselasky *	      unix commands (XK_EXE). It contains the
12290650Shselasky *	      following externally visible functions.
13290650Shselasky *
14290650Shselasky *		int GetXkey(ch,val);
15290650Shselasky *		CStr *ch;
16290650Shselasky *		XmapVal *val;
17290650Shselasky *
18290650Shselasky *	      Looks up *ch in map and then reads characters until a
19290650Shselasky *	      complete match is found or a mismatch occurs. Returns the
20290650Shselasky *	      type of the match found (XK_STR, XK_CMD, or XK_EXE).
21290650Shselasky *	      Returns NULL in val.str and XK_STR for no match.
22290650Shselasky *	      The last character read is returned in *ch.
23290650Shselasky *
24290650Shselasky *		void AddXkey(Xkey, val, ntype);
25290650Shselasky *		CStr *Xkey;
26290650Shselasky *		XmapVal *val;
27290650Shselasky *		int ntype;
28290650Shselasky *
29290650Shselasky *	      Adds Xkey to the Xmap and associates the value in val with it.
30290650Shselasky *	      If Xkey is already is in Xmap, the new code is applied to the
31290650Shselasky *	      existing Xkey. Ntype specifies if code is a command, an
32290650Shselasky *	      out string or a unix command.
33290650Shselasky *
34290650Shselasky *	        int DeleteXkey(Xkey);
35290650Shselasky *	        CStr *Xkey;
36290650Shselasky *
37290650Shselasky *	      Delete the Xkey and all longer Xkeys staring with Xkey, if
38290650Shselasky *	      they exists.
39290650Shselasky *
40290650Shselasky *	      Warning:
41290650Shselasky *		If Xkey is a substring of some other Xkeys, then the longer
42290650Shselasky *		Xkeys are lost!!  That is, if the Xkeys "abcd" and "abcef"
43290650Shselasky *		are in Xmap, adding the key "abc" will cause the first two
44290650Shselasky *		definitions to be lost.
45290650Shselasky *
46290650Shselasky *		void ResetXmap();
47290650Shselasky *
48290650Shselasky *	      Removes all entries from Xmap and resets the defaults.
49290650Shselasky *
50290650Shselasky *		void PrintXkey(Xkey);
51290650Shselasky *		CStr *Xkey;
52290650Shselasky *
53290650Shselasky *	      Prints all extended keys prefixed by Xkey and their associated
54290650Shselasky *	      commands.
55290650Shselasky *
56290650Shselasky *	      Restrictions:
57290650Shselasky *	      -------------
58290650Shselasky *	        1) It is not possible to have one Xkey that is a
59290650Shselasky *		   substring of another.
60290650Shselasky */
61290650Shselasky/*-
62290650Shselasky * Copyright (c) 1980, 1991 The Regents of the University of California.
63290650Shselasky * All rights reserved.
64290650Shselasky *
65290650Shselasky * Redistribution and use in source and binary forms, with or without
66290650Shselasky * modification, are permitted provided that the following conditions
67290650Shselasky * are met:
68290650Shselasky * 1. Redistributions of source code must retain the above copyright
69290650Shselasky *    notice, this list of conditions and the following disclaimer.
70290650Shselasky * 2. Redistributions in binary form must reproduce the above copyright
71290650Shselasky *    notice, this list of conditions and the following disclaimer in the
72290650Shselasky *    documentation and/or other materials provided with the distribution.
73290650Shselasky * 3. Neither the name of the University nor the names of its contributors
74290650Shselasky *    may be used to endorse or promote products derived from this software
75290650Shselasky *    without specific prior written permission.
76290650Shselasky *
77290650Shselasky * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
78290650Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79290650Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80290650Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
81290650Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82290650Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83290650Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84290650Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85290650Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86290650Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87290650Shselasky * SUCH DAMAGE.
88290650Shselasky */
89290650Shselasky#include "sh.h"
90290650Shselasky
91290650ShselaskyRCSID("$tcsh: ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $")
92290650Shselasky
93290650Shselasky#include "ed.h"
94290650Shselasky#include "ed.defns.h"
95290650Shselasky
96290650Shselasky#ifndef NULL
97290650Shselasky#define NULL 0
98290650Shselasky#endif
99290650Shselasky
100290650Shselasky/* Internal Data types and declarations */
101290650Shselasky
102290650Shselasky/* The Nodes of the Xmap.  The Xmap is a linked list of these node
103290650Shselasky * elements
104290650Shselasky */
105290650Shselaskytypedef struct Xmapnode {
106290650Shselasky    Char    ch;			/* single character of Xkey */
107290650Shselasky    int     type;
108290650Shselasky    XmapVal val; 		/* command code or pointer to string, if this
109290650Shselasky				 * is a leaf */
110290650Shselasky    struct Xmapnode *next;	/* ptr to next char of this Xkey */
111290650Shselasky    struct Xmapnode *sibling;	/* ptr to another Xkey with same prefix */
112290650Shselasky} XmapNode;
113290650Shselasky
114290650Shselaskystatic XmapNode *Xmap = NULL;	/* the current Xmap */
115290650Shselasky
116290650Shselasky
117290650Shselasky/* Some declarations of procedures */
118290650Shselaskystatic	int       TraverseMap	(XmapNode *, CStr *, XmapVal *);
119290650Shselaskystatic	int       TryNode	(XmapNode *, CStr *, XmapVal *, int);
120290650Shselaskystatic	XmapNode *GetFreeNode	(CStr *);
121290650Shselaskystatic	void	  PutFreeNode	(XmapNode *);
122290650Shselaskystatic	int	  TryDeleteNode	(XmapNode **, CStr *);
123290650Shselaskystatic	int	  Lookup	(struct Strbuf *, const CStr *,
124290650Shselasky				 const XmapNode *);
125290650Shselaskystatic	void	  Enumerate	(struct Strbuf *, const XmapNode *);
126290650Shselaskystatic	void	  unparsech	(struct Strbuf *, Char);
127290650Shselasky
128290650Shselasky
129290650ShselaskyXmapVal *
130290650ShselaskyXmapCmd(int cmd)
131290650Shselasky{
132290650Shselasky    static XmapVal xm;
133290650Shselasky    xm.cmd = (KEYCMD) cmd;
134290650Shselasky    return &xm;
135290650Shselasky}
136290650Shselasky
137290650ShselaskyXmapVal *
138290650ShselaskyXmapStr(CStr *str)
139290650Shselasky{
140290650Shselasky    static XmapVal xm;
141290650Shselasky    xm.str.len = str->len;
142290650Shselasky    xm.str.buf = str->buf;
143290650Shselasky    return &xm;
144290650Shselasky}
145290650Shselasky
146290650Shselasky/* ResetXmap():
147290650Shselasky *	Takes all nodes on Xmap and puts them on free list.  Then
148290650Shselasky *	initializes Xmap with arrow keys
149290650Shselasky */
150290650Shselaskyvoid
151290650ShselaskyResetXmap(void)
152290650Shselasky{
153290650Shselasky    PutFreeNode(Xmap);
154290650Shselasky    Xmap = NULL;
155290650Shselasky
156290650Shselasky    DefaultArrowKeys();
157290650Shselasky    return;
158290650Shselasky}
159290650Shselasky
160290650Shselasky
161290650Shselasky/* GetXkey():
162290650Shselasky *	Calls the recursive function with entry point Xmap
163290650Shselasky */
164290650Shselaskyint
165290650ShselaskyGetXkey(CStr *ch, XmapVal *val)
166290650Shselasky{
167290650Shselasky    return (TraverseMap(Xmap, ch, val));
168290650Shselasky}
169290650Shselasky
170290650Shselasky/* TraverseMap():
171290650Shselasky *	recursively traverses node in tree until match or mismatch is
172290650Shselasky * 	found.  May read in more characters.
173290650Shselasky */
174290650Shselaskystatic int
175290650ShselaskyTraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
176290650Shselasky{
177290650Shselasky    Char    tch;
178290650Shselasky
179290650Shselasky    if (ptr->ch == *(ch->buf)) {
180290650Shselasky	/* match found */
181290650Shselasky	if (ptr->next) {
182290650Shselasky	    /* Xkey not complete so get next char */
183290650Shselasky	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
184290650Shselasky		val->cmd = F_SEND_EOF;
185290650Shselasky		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
186290650Shselasky	    }
187290650Shselasky	    *(ch->buf) = tch;
188290650Shselasky	    return (TraverseMap(ptr->next, ch, val));
189290650Shselasky	}
190290650Shselasky	else {
191290650Shselasky	    *val = ptr->val;
192290650Shselasky	    if (ptr->type != XK_CMD)
193290650Shselasky		*(ch->buf) = '\0';
194290650Shselasky	    return ptr->type;
195290650Shselasky	}
196290650Shselasky    }
197290650Shselasky    else {
198290650Shselasky	/* no match found here */
199290650Shselasky	if (ptr->sibling) {
200290650Shselasky	    /* try next sibling */
201290650Shselasky	    return (TraverseMap(ptr->sibling, ch, val));
202290650Shselasky	}
203290650Shselasky	else {
204290650Shselasky	    /* no next sibling -- mismatch */
205290650Shselasky	    val->str.buf = NULL;
206290650Shselasky	    val->str.len = 0;
207290650Shselasky	    return XK_STR;
208290650Shselasky	}
209290650Shselasky    }
210290650Shselasky}
211290650Shselasky
212290650Shselaskyvoid
213290650ShselaskyAddXkey(const CStr *Xkey, XmapVal *val, int ntype)
214290650Shselasky{
215290650Shselasky    CStr cs;
216290650Shselasky    cs.buf = Xkey->buf;
217290650Shselasky    cs.len = Xkey->len;
218290650Shselasky    if (Xkey->len == 0) {
219290650Shselasky	xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
220290650Shselasky	return;
221290650Shselasky    }
222290650Shselasky
223290650Shselasky    if (ntype == XK_CMD && val->cmd == F_XKEY) {
224290650Shselasky	xprintf("%s",
225290650Shselasky	    CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
226290650Shselasky	return;
227290650Shselasky    }
228290650Shselasky
229290650Shselasky    if (Xmap == NULL)
230290650Shselasky	/* tree is initially empty.  Set up new node to match Xkey[0] */
231290650Shselasky	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
232290650Shselasky
233290650Shselasky    /* Now recurse through Xmap */
234290650Shselasky    (void) TryNode(Xmap, &cs, val, ntype);
235290650Shselasky    return;
236290650Shselasky}
237290650Shselasky
238290650Shselaskystatic int
239290650ShselaskyTryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
240290650Shselasky{
241290650Shselasky    /*
242290650Shselasky     * Find a node that matches *string or allocate a new one
243290650Shselasky     */
244290650Shselasky    if (ptr->ch != *(str->buf)) {
245290650Shselasky	XmapNode *xm;
246290650Shselasky
247290650Shselasky	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
248290650Shselasky	    if (xm->sibling->ch == *(str->buf))
249290650Shselasky		break;
250290650Shselasky	if (xm->sibling == NULL)
251290650Shselasky	    xm->sibling = GetFreeNode(str);	/* setup new node */
252290650Shselasky	ptr = xm->sibling;
253290650Shselasky    }
254290650Shselasky
255290650Shselasky    str->buf++;
256290650Shselasky    str->len--;
257290650Shselasky    if (str->len == 0) {
258290650Shselasky	size_t len;
259290650Shselasky
260290650Shselasky	/* we're there */
261290650Shselasky	if (ptr->next != NULL) {
262290650Shselasky	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
263290650Shselasky	    ptr->next = NULL;
264290650Shselasky	}
265290650Shselasky
266290650Shselasky	switch (ptr->type) {
267290650Shselasky	case XK_STR:
268290650Shselasky	case XK_EXE:
269290650Shselasky	    xfree(ptr->val.str.buf);
270290650Shselasky	    ptr->val.str.len = 0;
271290650Shselasky	    break;
272290650Shselasky	case XK_NOD:
273290650Shselasky	case XK_CMD:
274290650Shselasky	    break;
275290650Shselasky	default:
276290650Shselasky	    abort();
277290650Shselasky	    break;
278290650Shselasky	}
279290650Shselasky
280290650Shselasky	switch (ptr->type = ntype) {
281290650Shselasky	case XK_CMD:
282290650Shselasky	    ptr->val = *val;
283290650Shselasky	    break;
284290650Shselasky	case XK_STR:
285290650Shselasky	case XK_EXE:
286290650Shselasky	    ptr->val.str.len = val->str.len;
287290650Shselasky	    len = (val->str.len + 1) * sizeof(*ptr->val.str.buf);
288290650Shselasky	    ptr->val.str.buf = xmalloc(len);
289290650Shselasky	    (void) memcpy(ptr->val.str.buf, val->str.buf, len);
290290650Shselasky	    break;
291290650Shselasky	default:
292290650Shselasky	    abort();
293290650Shselasky	    break;
294290650Shselasky	}
295290650Shselasky    }
296290650Shselasky    else {
297290650Shselasky	/* still more chars to go */
298290650Shselasky	if (ptr->next == NULL)
299290650Shselasky	    ptr->next = GetFreeNode(str);	/* setup new node */
300290650Shselasky	(void) TryNode(ptr->next, str, val, ntype);
301290650Shselasky    }
302290650Shselasky    return (0);
303290650Shselasky}
304290650Shselasky
305290650Shselaskyvoid
306290650ShselaskyClearXkey(KEYCMD *map, const CStr *in)
307290650Shselasky{
308290650Shselasky    unsigned char c = (unsigned char) *(in->buf);
309290650Shselasky    if ((map[c] == F_XKEY) &&
310290650Shselasky	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
311290650Shselasky	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
312290650Shselasky	(void) DeleteXkey(in);
313290650Shselasky}
314290650Shselasky
315290650Shselaskyint
316290650ShselaskyDeleteXkey(const CStr *Xkey)
317290650Shselasky{
318290650Shselasky    CStr s;
319290650Shselasky
320290650Shselasky    s = *Xkey;
321290650Shselasky    if (s.len == 0) {
322290650Shselasky	xprintf("%s",
323290650Shselasky	        CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
324290650Shselasky	return (-1);
325290650Shselasky    }
326290650Shselasky
327290650Shselasky    if (Xmap == NULL)
328290650Shselasky	return (0);
329290650Shselasky
330290650Shselasky    (void) TryDeleteNode(&Xmap, &s);
331290650Shselasky    return (0);
332290650Shselasky}
333290650Shselasky
334290650Shselasky/* Destroys str */
335290650Shselaskystatic int
336290650ShselaskyTryDeleteNode(XmapNode **inptr, CStr *str)
337290650Shselasky{
338290650Shselasky    XmapNode *ptr;
339290650Shselasky
340290650Shselasky    ptr = *inptr;
341290650Shselasky    /*
342290650Shselasky     * Find a node that matches *string or allocate a new one
343290650Shselasky     */
344290650Shselasky    if (ptr->ch != *(str->buf)) {
345290650Shselasky	XmapNode *xm;
346290650Shselasky
347290650Shselasky	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
348290650Shselasky	    if (xm->sibling->ch == *(str->buf))
349290650Shselasky		break;
350290650Shselasky	if (xm->sibling == NULL)
351290650Shselasky	    return (0);
352290650Shselasky	inptr = &xm->sibling;
353290650Shselasky	ptr = xm->sibling;
354290650Shselasky    }
355290650Shselasky
356290650Shselasky    str->buf++;
357290650Shselasky    str->len--;
358290650Shselasky
359290650Shselasky    if (str->len == 0) {
360290650Shselasky	/* we're there */
361290650Shselasky	*inptr = ptr->sibling;
362290650Shselasky	ptr->sibling = NULL;
363290650Shselasky	PutFreeNode(ptr);
364290650Shselasky	return (1);
365290650Shselasky    }
366290650Shselasky    else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
367290650Shselasky	if (ptr->next != NULL)
368290650Shselasky	    return (0);
369290650Shselasky	*inptr = ptr->sibling;
370290650Shselasky	ptr->sibling = NULL;
371290650Shselasky	PutFreeNode(ptr);
372290650Shselasky	return (1);
373290650Shselasky    }
374290650Shselasky    else {
375290650Shselasky	return (0);
376290650Shselasky    }
377290650Shselasky}
378290650Shselasky
379290650Shselasky/* PutFreeNode():
380290650Shselasky *	Puts a tree of nodes onto free list using free(3).
381290650Shselasky */
382290650Shselaskystatic void
383290650ShselaskyPutFreeNode(XmapNode *ptr)
384290650Shselasky{
385290650Shselasky    if (ptr == NULL)
386290650Shselasky	return;
387290650Shselasky
388290650Shselasky    if (ptr->next != NULL) {
389290650Shselasky	PutFreeNode(ptr->next);
390290650Shselasky	ptr->next = NULL;
391290650Shselasky    }
392290650Shselasky
393290650Shselasky    PutFreeNode(ptr->sibling);
394290650Shselasky
395290650Shselasky    switch (ptr->type) {
396290650Shselasky    case XK_CMD:
397290650Shselasky    case XK_NOD:
398290650Shselasky	break;
399290650Shselasky    case XK_EXE:
400290650Shselasky    case XK_STR:
401290650Shselasky	xfree(ptr->val.str.buf);
402290650Shselasky	break;
403290650Shselasky    default:
404290650Shselasky	abort();
405290650Shselasky	break;
406290650Shselasky    }
407290650Shselasky    xfree(ptr);
408290650Shselasky}
409290650Shselasky
410290650Shselasky
411290650Shselasky/* GetFreeNode():
412290650Shselasky *	Returns pointer to an XmapNode for ch.
413290650Shselasky */
414290650Shselaskystatic XmapNode *
415290650ShselaskyGetFreeNode(CStr *ch)
416290650Shselasky{
417290650Shselasky    XmapNode *ptr;
418290650Shselasky
419290650Shselasky    ptr = xmalloc(sizeof(XmapNode));
420290650Shselasky    ptr->ch = ch->buf[0];
421290650Shselasky    ptr->type = XK_NOD;
422290650Shselasky    ptr->val.str.buf = NULL;
423290650Shselasky    ptr->val.str.len = 0;
424290650Shselasky    ptr->next = NULL;
425290650Shselasky    ptr->sibling = NULL;
426290650Shselasky    return (ptr);
427290650Shselasky}
428290650Shselasky
429290650Shselasky
430290650Shselasky/* PrintXKey():
431290650Shselasky *	Print the binding associated with Xkey key.
432290650Shselasky *	Print entire Xmap if null
433290650Shselasky */
434290650Shselaskyvoid
435290650ShselaskyPrintXkey(const CStr *key)
436290650Shselasky{
437290650Shselasky    struct Strbuf buf = Strbuf_INIT;
438290650Shselasky    CStr cs;
439290650Shselasky
440290650Shselasky    if (key) {
441290650Shselasky	cs.buf = key->buf;
442290650Shselasky	cs.len = key->len;
443290650Shselasky    }
444290650Shselasky    else {
445290650Shselasky	cs.buf = STRNULL;
446290650Shselasky	cs.len = 0;
447290650Shselasky    }
448290650Shselasky    /* do nothing if Xmap is empty and null key specified */
449290650Shselasky    if (Xmap == NULL && cs.len == 0)
450290650Shselasky	return;
451290650Shselasky
452290650Shselasky    Strbuf_append1(&buf, '"');
453290650Shselasky    cleanup_push(&buf, Strbuf_cleanup);
454290650Shselasky    if (Lookup(&buf, &cs, Xmap) <= -1)
455290650Shselasky	/* key is not bound */
456290650Shselasky	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
457290650Shselasky    cleanup_until(&buf);
458290650Shselasky}
459290650Shselasky
460290650Shselasky/* Lookup():
461290650Shselasky *	look for the string starting at node ptr.
462290650Shselasky *	Print if last node
463290650Shselasky */
464290650Shselaskystatic int
465290650ShselaskyLookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
466290650Shselasky{
467290650Shselasky    if (ptr == NULL)
468290650Shselasky	return (-1);		/* cannot have null ptr */
469290650Shselasky
470290650Shselasky    if (str->len == 0) {
471290650Shselasky	/* no more chars in string.  Enumerate from here. */
472290650Shselasky	Enumerate(buf, ptr);
473290650Shselasky	return (0);
474290650Shselasky    }
475290650Shselasky    else {
476290650Shselasky	/* If match put this char into buf.  Recurse */
477290650Shselasky	if (ptr->ch == *(str->buf)) {
478290650Shselasky	    /* match found */
479290650Shselasky	    unparsech(buf, ptr->ch);
480290650Shselasky	    if (ptr->next != NULL) {
481290650Shselasky		/* not yet at leaf */
482290650Shselasky		CStr tstr;
483290650Shselasky		tstr.buf = str->buf + 1;
484290650Shselasky		tstr.len = str->len - 1;
485290650Shselasky		return (Lookup(buf, &tstr, ptr->next));
486290650Shselasky	    }
487290650Shselasky	    else {
488290650Shselasky		/* next node is null so key should be complete */
489290650Shselasky		if (str->len == 1) {
490290650Shselasky		    Strbuf_append1(buf, '"');
491290650Shselasky		    Strbuf_terminate(buf);
492290650Shselasky		    printOne(buf->s, &ptr->val, ptr->type);
493290650Shselasky		    return (0);
494290650Shselasky		}
495290650Shselasky		else
496290650Shselasky		    return (-1);/* mismatch -- string still has chars */
497290650Shselasky	    }
498290650Shselasky	}
499290650Shselasky	else {
500290650Shselasky	    /* no match found try sibling */
501290650Shselasky	    if (ptr->sibling)
502290650Shselasky		return (Lookup(buf, str, ptr->sibling));
503290650Shselasky	    else
504290650Shselasky		return (-1);
505290650Shselasky	}
506290650Shselasky    }
507290650Shselasky}
508290650Shselasky
509290650Shselaskystatic void
510290650ShselaskyEnumerate(struct Strbuf *buf, const XmapNode *ptr)
511290650Shselasky{
512290650Shselasky    size_t old_len;
513290650Shselasky
514290650Shselasky    if (ptr == NULL) {
515290650Shselasky#ifdef DEBUG_EDIT
516290650Shselasky	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
517290650Shselasky#endif
518290650Shselasky	return;
519290650Shselasky    }
520290650Shselasky
521290650Shselasky    old_len = buf->len;
522290650Shselasky    unparsech(buf, ptr->ch); /* put this char at end of string */
523290650Shselasky    if (ptr->next == NULL) {
524290650Shselasky	/* print this Xkey and function */
525290650Shselasky	Strbuf_append1(buf, '"');
526290650Shselasky	Strbuf_terminate(buf);
527290650Shselasky	printOne(buf->s, &ptr->val, ptr->type);
528290650Shselasky    }
529290650Shselasky    else
530290650Shselasky	Enumerate(buf, ptr->next);
531290650Shselasky
532290650Shselasky    /* go to sibling if there is one */
533290650Shselasky    if (ptr->sibling) {
534290650Shselasky	buf->len = old_len;
535290650Shselasky	Enumerate(buf, ptr->sibling);
536290650Shselasky    }
537290650Shselasky}
538290650Shselasky
539290650Shselasky
540290650Shselasky/* PrintOne():
541290650Shselasky *	Print the specified key and its associated
542290650Shselasky *	function specified by val
543290650Shselasky */
544290650Shselaskyvoid
545290650ShselaskyprintOne(const Char *key, const XmapVal *val, int ntype)
546290650Shselasky{
547290650Shselasky    struct KeyFuncs *fp;
548290650Shselasky    static const char *fmt = "%s\n";
549290650Shselasky
550290650Shselasky    xprintf("%-15S-> ", key);
551290650Shselasky    if (val != NULL)
552290650Shselasky	switch (ntype) {
553290650Shselasky	case XK_STR:
554290650Shselasky	case XK_EXE: {
555290650Shselasky	    unsigned char *p;
556290650Shselasky
557290650Shselasky	    p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
558290650Shselasky	    cleanup_push(p, xfree);
559290650Shselasky	    xprintf(fmt, p);
560290650Shselasky	    cleanup_until(p);
561290650Shselasky	    break;
562290650Shselasky	}
563290650Shselasky	case XK_CMD:
564290650Shselasky	    for (fp = FuncNames; fp->name; fp++)
565290650Shselasky		if (val->cmd == fp->func)
566290650Shselasky		    xprintf(fmt, fp->name);
567290650Shselasky		break;
568290650Shselasky	default:
569290650Shselasky	    abort();
570290650Shselasky	    break;
571290650Shselasky	}
572290650Shselasky    else
573290650Shselasky	xprintf(fmt, CGETS(9, 7, "no input"));
574290650Shselasky}
575290650Shselasky
576290650Shselaskystatic void
577290650Shselaskyunparsech(struct Strbuf *buf, Char ch)
578290650Shselasky{
579290650Shselasky    if (ch == 0) {
580290650Shselasky	Strbuf_append1(buf, '^');
581290650Shselasky	Strbuf_append1(buf, '@');
582290650Shselasky    }
583290650Shselasky    else if (Iscntrl(ch)) {
584290650Shselasky	Strbuf_append1(buf, '^');
585290650Shselasky	if (ch == CTL_ESC('\177'))
586290650Shselasky	    Strbuf_append1(buf, '?');
587290650Shselasky	else
588290650Shselasky#ifdef IS_ASCII
589290650Shselasky	    Strbuf_append1(buf, ch | 0100);
590290650Shselasky#else
591290650Shselasky	    Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
592290650Shselasky#endif
593290650Shselasky    }
594290650Shselasky    else if (ch == '^') {
595290650Shselasky	Strbuf_append1(buf, '\\');
596290650Shselasky	Strbuf_append1(buf, '^');
597290650Shselasky    } else if (ch == '\\') {
598290650Shselasky	Strbuf_append1(buf, '\\');
599290650Shselasky	Strbuf_append1(buf, '\\');
600290650Shselasky    } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
601290650Shselasky	Strbuf_append1(buf, ch);
602290650Shselasky    }
603290650Shselasky    else {
604290650Shselasky	Strbuf_append1(buf, '\\');
605290650Shselasky	Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
606290650Shselasky	Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
607290650Shselasky	Strbuf_append1(buf, (ch & 7) + '0');
608290650Shselasky    }
609290650Shselasky}
610290650Shselasky
611290650ShselaskyeChar
612290650Shselaskyparseescape(const Char **ptr)
613290650Shselasky{
614290650Shselasky    const Char *p;
615290650Shselasky    Char c;
616290650Shselasky
617290650Shselasky    p = *ptr;
618290650Shselasky
619290650Shselasky    if ((p[1] & CHAR) == 0) {
620290650Shselasky	xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
621290650Shselasky	return CHAR_ERR;
622290650Shselasky    }
623290650Shselasky    if ((*p & CHAR) == '\\') {
624290650Shselasky	p++;
625290650Shselasky	switch (*p & CHAR) {
626290650Shselasky	case 'a':
627290650Shselasky	    c = CTL_ESC('\007');         /* Bell */
628290650Shselasky	    break;
629290650Shselasky	case 'b':
630290650Shselasky	    c = CTL_ESC('\010');         /* Backspace */
631290650Shselasky	    break;
632290650Shselasky	case 'e':
633290650Shselasky	    c = CTL_ESC('\033');         /* Escape */
634290650Shselasky	    break;
635290650Shselasky	case 'f':
636290650Shselasky	    c = CTL_ESC('\014');         /* Form Feed */
637290650Shselasky	    break;
638290650Shselasky	case 'n':
639290650Shselasky	    c = CTL_ESC('\012');         /* New Line */
640290650Shselasky	    break;
641290650Shselasky	case 'r':
642290650Shselasky	    c = CTL_ESC('\015');         /* Carriage Return */
643290650Shselasky	    break;
644290650Shselasky	case 't':
645290650Shselasky	    c = CTL_ESC('\011');         /* Horizontal Tab */
646290650Shselasky	    break;
647290650Shselasky	case 'v':
648290650Shselasky	    c = CTL_ESC('\013');         /* Vertical Tab */
649290650Shselasky	    break;
650290650Shselasky	case '\\':
651290650Shselasky	    c = '\\';
652290650Shselasky	    break;
653290650Shselasky	case '0':
654290650Shselasky	case '1':
655290650Shselasky	case '2':
656290650Shselasky	case '3':
657290650Shselasky	case '4':
658290650Shselasky	case '5':
659290650Shselasky	case '6':
660290650Shselasky	case '7':
661290650Shselasky	    {
662290650Shselasky		int cnt, val;
663290650Shselasky		Char ch;
664290650Shselasky
665290650Shselasky		for (cnt = 0, val = 0; cnt < 3; cnt++) {
666290650Shselasky		    ch = *p++ & CHAR;
667290650Shselasky		    if (ch < '0' || ch > '7') {
668290650Shselasky			p--;
669290650Shselasky			break;
670290650Shselasky		    }
671290650Shselasky		    val = (val << 3) | (ch - '0');
672290650Shselasky		}
673290650Shselasky		if ((val & ~0xff) != 0) {
674290650Shselasky		    xprintf("%s", CGETS(9, 9,
675290650Shselasky			    "Octal constant does not fit in a char.\n"));
676290650Shselasky		    return 0;
677290650Shselasky		}
678290650Shselasky#ifndef IS_ASCII
679290650Shselasky		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
680290650Shselasky		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
681290650Shselasky			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
682290650Shselasky#endif
683290650Shselasky		c = (Char) val;
684290650Shselasky		--p;
685290650Shselasky	    }
686290650Shselasky	    break;
687290650Shselasky	default:
688290650Shselasky	    c = *p;
689290650Shselasky	    break;
690290650Shselasky	}
691290650Shselasky    }
692290650Shselasky    else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
693290650Shselasky				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
694290650Shselasky	p++;
695290650Shselasky#ifdef IS_ASCII
696290650Shselasky	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
697290650Shselasky#else
698290650Shselasky	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
699290650Shselasky	if (adrof(STRwarnebcdic))
700290650Shselasky	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
701290650Shselasky		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
702290650Shselasky#endif
703290650Shselasky    }
704290650Shselasky    else
705290650Shselasky	c = *p;
706290650Shselasky    *ptr = p;
707290650Shselasky    return (c);
708290650Shselasky}
709290650Shselasky
710290650Shselasky
711290650Shselaskyunsigned char *
712290650Shselaskyunparsestring(const CStr *str, const Char *sep)
713290650Shselasky{
714290650Shselasky    unsigned char *buf, *b;
715290650Shselasky    Char   p;
716290650Shselasky    int l;
717290650Shselasky
718290650Shselasky    /* Worst-case is "\uuu" or result of wctomb() for each char from str */
719290650Shselasky    buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
720290650Shselasky    b = buf;
721290650Shselasky    if (sep[0])
722290650Shselasky#ifndef WINNT_NATIVE
723290650Shselasky	*b++ = sep[0];
724290650Shselasky#else /* WINNT_NATIVE */
725290650Shselasky	*b++ = CHAR & sep[0];
726290650Shselasky#endif /* !WINNT_NATIVE */
727290650Shselasky
728290650Shselasky    for (l = 0; l < str->len; l++) {
729290650Shselasky	p = str->buf[l];
730290650Shselasky	if (Iscntrl(p)) {
731290650Shselasky	    *b++ = '^';
732290650Shselasky	    if (p == CTL_ESC('\177'))
733290650Shselasky		*b++ = '?';
734290650Shselasky	    else
735290650Shselasky#ifdef IS_ASCII
736290650Shselasky		*b++ = (unsigned char) (p | 0100);
737290650Shselasky#else
738290650Shselasky		*b++ = _toebcdic[_toascii[p]|0100];
739290650Shselasky#endif
740290650Shselasky	}
741290650Shselasky	else if (p == '^' || p == '\\') {
742290650Shselasky	    *b++ = '\\';
743290650Shselasky	    *b++ = (unsigned char) p;
744290650Shselasky	}
745290650Shselasky	else if (p == ' ' || (Isprint(p) && !Isspace(p)))
746290650Shselasky	    b += one_wctomb((char *)b, p & CHAR);
747290650Shselasky	else {
748290650Shselasky	    *b++ = '\\';
749290650Shselasky	    *b++ = ((p >> 6) & 7) + '0';
750290650Shselasky	    *b++ = ((p >> 3) & 7) + '0';
751290650Shselasky	    *b++ = (p & 7) + '0';
752290650Shselasky	}
753290650Shselasky    }
754290650Shselasky    if (sep[0] && sep[1])
755290650Shselasky#ifndef WINNT_NATIVE
756290650Shselasky	*b++ = sep[1];
757290650Shselasky#else /* WINNT_NATIVE */
758290650Shselasky	*b++ = CHAR & sep[1];
759290650Shselasky#endif /* !WINNT_NATIVE */
760290650Shselasky    *b++ = 0;
761290650Shselasky    return buf;			/* should check for overflow */
762290650Shselasky}
763290650Shselasky