xref: /freebsd/contrib/tcsh/ed.xmap.c (revision 87c1627502a5dde91e5284118eec8682b60f27a2)
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $ */
2 /*
3  * ed.xmap.c: This module contains the procedures for maintaining
4  *	      the extended-key map.
5  *
6  * 	      An extended-key (Xkey) is a sequence of keystrokes
7  *	      introduced with an sequence introducer and consisting
8  *	      of an arbitrary number of characters.  This module maintains
9  *	      a map (the Xmap) to convert these extended-key sequences
10  * 	      into input strings (XK_STR), editor functions (XK_CMD), or
11  *	      unix commands (XK_EXE). It contains the
12  *	      following externally visible functions.
13  *
14  *		int GetXkey(ch,val);
15  *		CStr *ch;
16  *		XmapVal *val;
17  *
18  *	      Looks up *ch in map and then reads characters until a
19  *	      complete match is found or a mismatch occurs. Returns the
20  *	      type of the match found (XK_STR, XK_CMD, or XK_EXE).
21  *	      Returns NULL in val.str and XK_STR for no match.
22  *	      The last character read is returned in *ch.
23  *
24  *		void AddXkey(Xkey, val, ntype);
25  *		CStr *Xkey;
26  *		XmapVal *val;
27  *		int ntype;
28  *
29  *	      Adds Xkey to the Xmap and associates the value in val with it.
30  *	      If Xkey is already is in Xmap, the new code is applied to the
31  *	      existing Xkey. Ntype specifies if code is a command, an
32  *	      out string or a unix command.
33  *
34  *	        int DeleteXkey(Xkey);
35  *	        CStr *Xkey;
36  *
37  *	      Delete the Xkey and all longer Xkeys staring with Xkey, if
38  *	      they exists.
39  *
40  *	      Warning:
41  *		If Xkey is a substring of some other Xkeys, then the longer
42  *		Xkeys are lost!!  That is, if the Xkeys "abcd" and "abcef"
43  *		are in Xmap, adding the key "abc" will cause the first two
44  *		definitions to be lost.
45  *
46  *		void ResetXmap();
47  *
48  *	      Removes all entries from Xmap and resets the defaults.
49  *
50  *		void PrintXkey(Xkey);
51  *		CStr *Xkey;
52  *
53  *	      Prints all extended keys prefixed by Xkey and their associated
54  *	      commands.
55  *
56  *	      Restrictions:
57  *	      -------------
58  *	        1) It is not possible to have one Xkey that is a
59  *		   substring of another.
60  */
61 /*-
62  * Copyright (c) 1980, 1991 The Regents of the University of California.
63  * All rights reserved.
64  *
65  * Redistribution and use in source and binary forms, with or without
66  * modification, are permitted provided that the following conditions
67  * are met:
68  * 1. Redistributions of source code must retain the above copyright
69  *    notice, this list of conditions and the following disclaimer.
70  * 2. Redistributions in binary form must reproduce the above copyright
71  *    notice, this list of conditions and the following disclaimer in the
72  *    documentation and/or other materials provided with the distribution.
73  * 3. Neither the name of the University nor the names of its contributors
74  *    may be used to endorse or promote products derived from this software
75  *    without specific prior written permission.
76  *
77  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
78  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
79  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
80  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
81  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
82  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
83  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
84  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
85  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
86  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
87  * SUCH DAMAGE.
88  */
89 #include "sh.h"
90 
91 RCSID("$tcsh: ed.xmap.c,v 3.37 2009/06/25 21:15:37 christos Exp $")
92 
93 #include "ed.h"
94 #include "ed.defns.h"
95 
96 #ifndef NULL
97 #define NULL 0
98 #endif
99 
100 /* Internal Data types and declarations */
101 
102 /* The Nodes of the Xmap.  The Xmap is a linked list of these node
103  * elements
104  */
105 typedef struct Xmapnode {
106     Char    ch;			/* single character of Xkey */
107     int     type;
108     XmapVal val; 		/* command code or pointer to string, if this
109 				 * is a leaf */
110     struct Xmapnode *next;	/* ptr to next char of this Xkey */
111     struct Xmapnode *sibling;	/* ptr to another Xkey with same prefix */
112 } XmapNode;
113 
114 static XmapNode *Xmap = NULL;	/* the current Xmap */
115 
116 
117 /* Some declarations of procedures */
118 static	int       TraverseMap	(XmapNode *, CStr *, XmapVal *);
119 static	int       TryNode	(XmapNode *, CStr *, XmapVal *, int);
120 static	XmapNode *GetFreeNode	(CStr *);
121 static	void	  PutFreeNode	(XmapNode *);
122 static	int	  TryDeleteNode	(XmapNode **, CStr *);
123 static	int	  Lookup	(struct Strbuf *, const CStr *,
124 				 const XmapNode *);
125 static	void	  Enumerate	(struct Strbuf *, const XmapNode *);
126 static	void	  unparsech	(struct Strbuf *, Char);
127 
128 
129 XmapVal *
130 XmapCmd(int cmd)
131 {
132     static XmapVal xm;
133     xm.cmd = (KEYCMD) cmd;
134     return &xm;
135 }
136 
137 XmapVal *
138 XmapStr(CStr *str)
139 {
140     static XmapVal xm;
141     xm.str.len = str->len;
142     xm.str.buf = str->buf;
143     return &xm;
144 }
145 
146 /* ResetXmap():
147  *	Takes all nodes on Xmap and puts them on free list.  Then
148  *	initializes Xmap with arrow keys
149  */
150 void
151 ResetXmap(void)
152 {
153     PutFreeNode(Xmap);
154     Xmap = NULL;
155 
156     DefaultArrowKeys();
157     return;
158 }
159 
160 
161 /* GetXkey():
162  *	Calls the recursive function with entry point Xmap
163  */
164 int
165 GetXkey(CStr *ch, XmapVal *val)
166 {
167     return (TraverseMap(Xmap, ch, val));
168 }
169 
170 /* TraverseMap():
171  *	recursively traverses node in tree until match or mismatch is
172  * 	found.  May read in more characters.
173  */
174 static int
175 TraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
176 {
177     Char    tch;
178 
179     if (ptr->ch == *(ch->buf)) {
180 	/* match found */
181 	if (ptr->next) {
182 	    /* Xkey not complete so get next char */
183 	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
184 		val->cmd = F_SEND_EOF;
185 		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
186 	    }
187 	    *(ch->buf) = tch;
188 	    return (TraverseMap(ptr->next, ch, val));
189 	}
190 	else {
191 	    *val = ptr->val;
192 	    if (ptr->type != XK_CMD)
193 		*(ch->buf) = '\0';
194 	    return ptr->type;
195 	}
196     }
197     else {
198 	/* no match found here */
199 	if (ptr->sibling) {
200 	    /* try next sibling */
201 	    return (TraverseMap(ptr->sibling, ch, val));
202 	}
203 	else {
204 	    /* no next sibling -- mismatch */
205 	    val->str.buf = NULL;
206 	    val->str.len = 0;
207 	    return XK_STR;
208 	}
209     }
210 }
211 
212 void
213 AddXkey(const CStr *Xkey, XmapVal *val, int ntype)
214 {
215     CStr cs;
216     cs.buf = Xkey->buf;
217     cs.len = Xkey->len;
218     if (Xkey->len == 0) {
219 	xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
220 	return;
221     }
222 
223     if (ntype == XK_CMD && val->cmd == F_XKEY) {
224 	xprintf("%s",
225 	    CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
226 	return;
227     }
228 
229     if (Xmap == NULL)
230 	/* tree is initially empty.  Set up new node to match Xkey[0] */
231 	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
232 
233     /* Now recurse through Xmap */
234     (void) TryNode(Xmap, &cs, val, ntype);
235     return;
236 }
237 
238 static int
239 TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
240 {
241     /*
242      * Find a node that matches *string or allocate a new one
243      */
244     if (ptr->ch != *(str->buf)) {
245 	XmapNode *xm;
246 
247 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
248 	    if (xm->sibling->ch == *(str->buf))
249 		break;
250 	if (xm->sibling == NULL)
251 	    xm->sibling = GetFreeNode(str);	/* setup new node */
252 	ptr = xm->sibling;
253     }
254 
255     str->buf++;
256     str->len--;
257     if (str->len == 0) {
258 	size_t len;
259 
260 	/* we're there */
261 	if (ptr->next != NULL) {
262 	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
263 	    ptr->next = NULL;
264 	}
265 
266 	switch (ptr->type) {
267 	case XK_STR:
268 	case XK_EXE:
269 	    xfree(ptr->val.str.buf);
270 	    ptr->val.str.len = 0;
271 	    break;
272 	case XK_NOD:
273 	case XK_CMD:
274 	    break;
275 	default:
276 	    abort();
277 	    break;
278 	}
279 
280 	switch (ptr->type = ntype) {
281 	case XK_CMD:
282 	    ptr->val = *val;
283 	    break;
284 	case XK_STR:
285 	case XK_EXE:
286 	    ptr->val.str.len = val->str.len;
287 	    len = (val->str.len + 1) * sizeof(*ptr->val.str.buf);
288 	    ptr->val.str.buf = xmalloc(len);
289 	    (void) memcpy(ptr->val.str.buf, val->str.buf, len);
290 	    break;
291 	default:
292 	    abort();
293 	    break;
294 	}
295     }
296     else {
297 	/* still more chars to go */
298 	if (ptr->next == NULL)
299 	    ptr->next = GetFreeNode(str);	/* setup new node */
300 	(void) TryNode(ptr->next, str, val, ntype);
301     }
302     return (0);
303 }
304 
305 void
306 ClearXkey(KEYCMD *map, const CStr *in)
307 {
308     unsigned char c = (unsigned char) *(in->buf);
309     if ((map[c] == F_XKEY) &&
310 	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
311 	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
312 	(void) DeleteXkey(in);
313 }
314 
315 int
316 DeleteXkey(const CStr *Xkey)
317 {
318     CStr s;
319 
320     s = *Xkey;
321     if (s.len == 0) {
322 	xprintf("%s",
323 	        CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
324 	return (-1);
325     }
326 
327     if (Xmap == NULL)
328 	return (0);
329 
330     (void) TryDeleteNode(&Xmap, &s);
331     return (0);
332 }
333 
334 /* Destroys str */
335 static int
336 TryDeleteNode(XmapNode **inptr, CStr *str)
337 {
338     XmapNode *ptr;
339 
340     ptr = *inptr;
341     /*
342      * Find a node that matches *string or allocate a new one
343      */
344     if (ptr->ch != *(str->buf)) {
345 	XmapNode *xm;
346 
347 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
348 	    if (xm->sibling->ch == *(str->buf))
349 		break;
350 	if (xm->sibling == NULL)
351 	    return (0);
352 	inptr = &xm->sibling;
353 	ptr = xm->sibling;
354     }
355 
356     str->buf++;
357     str->len--;
358 
359     if (str->len == 0) {
360 	/* we're there */
361 	*inptr = ptr->sibling;
362 	ptr->sibling = NULL;
363 	PutFreeNode(ptr);
364 	return (1);
365     }
366     else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
367 	if (ptr->next != NULL)
368 	    return (0);
369 	*inptr = ptr->sibling;
370 	ptr->sibling = NULL;
371 	PutFreeNode(ptr);
372 	return (1);
373     }
374     else {
375 	return (0);
376     }
377 }
378 
379 /* PutFreeNode():
380  *	Puts a tree of nodes onto free list using free(3).
381  */
382 static void
383 PutFreeNode(XmapNode *ptr)
384 {
385     if (ptr == NULL)
386 	return;
387 
388     if (ptr->next != NULL) {
389 	PutFreeNode(ptr->next);
390 	ptr->next = NULL;
391     }
392 
393     PutFreeNode(ptr->sibling);
394 
395     switch (ptr->type) {
396     case XK_CMD:
397     case XK_NOD:
398 	break;
399     case XK_EXE:
400     case XK_STR:
401 	xfree(ptr->val.str.buf);
402 	break;
403     default:
404 	abort();
405 	break;
406     }
407     xfree(ptr);
408 }
409 
410 
411 /* GetFreeNode():
412  *	Returns pointer to an XmapNode for ch.
413  */
414 static XmapNode *
415 GetFreeNode(CStr *ch)
416 {
417     XmapNode *ptr;
418 
419     ptr = xmalloc(sizeof(XmapNode));
420     ptr->ch = ch->buf[0];
421     ptr->type = XK_NOD;
422     ptr->val.str.buf = NULL;
423     ptr->val.str.len = 0;
424     ptr->next = NULL;
425     ptr->sibling = NULL;
426     return (ptr);
427 }
428 
429 
430 /* PrintXKey():
431  *	Print the binding associated with Xkey key.
432  *	Print entire Xmap if null
433  */
434 void
435 PrintXkey(const CStr *key)
436 {
437     struct Strbuf buf = Strbuf_INIT;
438     CStr cs;
439 
440     if (key) {
441 	cs.buf = key->buf;
442 	cs.len = key->len;
443     }
444     else {
445 	cs.buf = STRNULL;
446 	cs.len = 0;
447     }
448     /* do nothing if Xmap is empty and null key specified */
449     if (Xmap == NULL && cs.len == 0)
450 	return;
451 
452     Strbuf_append1(&buf, '"');
453     cleanup_push(&buf, Strbuf_cleanup);
454     if (Lookup(&buf, &cs, Xmap) <= -1)
455 	/* key is not bound */
456 	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
457     cleanup_until(&buf);
458 }
459 
460 /* Lookup():
461  *	look for the string starting at node ptr.
462  *	Print if last node
463  */
464 static int
465 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
466 {
467     if (ptr == NULL)
468 	return (-1);		/* cannot have null ptr */
469 
470     if (str->len == 0) {
471 	/* no more chars in string.  Enumerate from here. */
472 	Enumerate(buf, ptr);
473 	return (0);
474     }
475     else {
476 	/* If match put this char into buf.  Recurse */
477 	if (ptr->ch == *(str->buf)) {
478 	    /* match found */
479 	    unparsech(buf, ptr->ch);
480 	    if (ptr->next != NULL) {
481 		/* not yet at leaf */
482 		CStr tstr;
483 		tstr.buf = str->buf + 1;
484 		tstr.len = str->len - 1;
485 		return (Lookup(buf, &tstr, ptr->next));
486 	    }
487 	    else {
488 		/* next node is null so key should be complete */
489 		if (str->len == 1) {
490 		    Strbuf_append1(buf, '"');
491 		    Strbuf_terminate(buf);
492 		    printOne(buf->s, &ptr->val, ptr->type);
493 		    return (0);
494 		}
495 		else
496 		    return (-1);/* mismatch -- string still has chars */
497 	    }
498 	}
499 	else {
500 	    /* no match found try sibling */
501 	    if (ptr->sibling)
502 		return (Lookup(buf, str, ptr->sibling));
503 	    else
504 		return (-1);
505 	}
506     }
507 }
508 
509 static void
510 Enumerate(struct Strbuf *buf, const XmapNode *ptr)
511 {
512     size_t old_len;
513 
514     if (ptr == NULL) {
515 #ifdef DEBUG_EDIT
516 	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
517 #endif
518 	return;
519     }
520 
521     old_len = buf->len;
522     unparsech(buf, ptr->ch); /* put this char at end of string */
523     if (ptr->next == NULL) {
524 	/* print this Xkey and function */
525 	Strbuf_append1(buf, '"');
526 	Strbuf_terminate(buf);
527 	printOne(buf->s, &ptr->val, ptr->type);
528     }
529     else
530 	Enumerate(buf, ptr->next);
531 
532     /* go to sibling if there is one */
533     if (ptr->sibling) {
534 	buf->len = old_len;
535 	Enumerate(buf, ptr->sibling);
536     }
537 }
538 
539 
540 /* PrintOne():
541  *	Print the specified key and its associated
542  *	function specified by val
543  */
544 void
545 printOne(const Char *key, const XmapVal *val, int ntype)
546 {
547     struct KeyFuncs *fp;
548     static const char *fmt = "%s\n";
549 
550     xprintf("%-15S-> ", key);
551     if (val != NULL)
552 	switch (ntype) {
553 	case XK_STR:
554 	case XK_EXE: {
555 	    unsigned char *p;
556 
557 	    p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
558 	    cleanup_push(p, xfree);
559 	    xprintf(fmt, p);
560 	    cleanup_until(p);
561 	    break;
562 	}
563 	case XK_CMD:
564 	    for (fp = FuncNames; fp->name; fp++)
565 		if (val->cmd == fp->func)
566 		    xprintf(fmt, fp->name);
567 		break;
568 	default:
569 	    abort();
570 	    break;
571 	}
572     else
573 	xprintf(fmt, CGETS(9, 7, "no input"));
574 }
575 
576 static void
577 unparsech(struct Strbuf *buf, Char ch)
578 {
579     if (ch == 0) {
580 	Strbuf_append1(buf, '^');
581 	Strbuf_append1(buf, '@');
582     }
583     else if (Iscntrl(ch)) {
584 	Strbuf_append1(buf, '^');
585 	if (ch == CTL_ESC('\177'))
586 	    Strbuf_append1(buf, '?');
587 	else
588 #ifdef IS_ASCII
589 	    Strbuf_append1(buf, ch | 0100);
590 #else
591 	    Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
592 #endif
593     }
594     else if (ch == '^') {
595 	Strbuf_append1(buf, '\\');
596 	Strbuf_append1(buf, '^');
597     } else if (ch == '\\') {
598 	Strbuf_append1(buf, '\\');
599 	Strbuf_append1(buf, '\\');
600     } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
601 	Strbuf_append1(buf, ch);
602     }
603     else {
604 	Strbuf_append1(buf, '\\');
605 	Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
606 	Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
607 	Strbuf_append1(buf, (ch & 7) + '0');
608     }
609 }
610 
611 eChar
612 parseescape(const Char **ptr)
613 {
614     const Char *p;
615     Char c;
616 
617     p = *ptr;
618 
619     if ((p[1] & CHAR) == 0) {
620 	xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
621 	return CHAR_ERR;
622     }
623     if ((*p & CHAR) == '\\') {
624 	p++;
625 	switch (*p & CHAR) {
626 	case 'a':
627 	    c = CTL_ESC('\007');         /* Bell */
628 	    break;
629 	case 'b':
630 	    c = CTL_ESC('\010');         /* Backspace */
631 	    break;
632 	case 'e':
633 	    c = CTL_ESC('\033');         /* Escape */
634 	    break;
635 	case 'f':
636 	    c = CTL_ESC('\014');         /* Form Feed */
637 	    break;
638 	case 'n':
639 	    c = CTL_ESC('\012');         /* New Line */
640 	    break;
641 	case 'r':
642 	    c = CTL_ESC('\015');         /* Carriage Return */
643 	    break;
644 	case 't':
645 	    c = CTL_ESC('\011');         /* Horizontal Tab */
646 	    break;
647 	case 'v':
648 	    c = CTL_ESC('\013');         /* Vertical Tab */
649 	    break;
650 	case '\\':
651 	    c = '\\';
652 	    break;
653 	case '0':
654 	case '1':
655 	case '2':
656 	case '3':
657 	case '4':
658 	case '5':
659 	case '6':
660 	case '7':
661 	    {
662 		int cnt, val;
663 		Char ch;
664 
665 		for (cnt = 0, val = 0; cnt < 3; cnt++) {
666 		    ch = *p++ & CHAR;
667 		    if (ch < '0' || ch > '7') {
668 			p--;
669 			break;
670 		    }
671 		    val = (val << 3) | (ch - '0');
672 		}
673 		if ((val & ~0xff) != 0) {
674 		    xprintf("%s", CGETS(9, 9,
675 			    "Octal constant does not fit in a char.\n"));
676 		    return 0;
677 		}
678 #ifndef IS_ASCII
679 		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
680 		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
681 			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
682 #endif
683 		c = (Char) val;
684 		--p;
685 	    }
686 	    break;
687 	default:
688 	    c = *p;
689 	    break;
690 	}
691     }
692     else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
693 				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
694 	p++;
695 #ifdef IS_ASCII
696 	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
697 #else
698 	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
699 	if (adrof(STRwarnebcdic))
700 	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
701 		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
702 #endif
703     }
704     else
705 	c = *p;
706     *ptr = p;
707     return (c);
708 }
709 
710 
711 unsigned char *
712 unparsestring(const CStr *str, const Char *sep)
713 {
714     unsigned char *buf, *b;
715     Char   p;
716     int l;
717 
718     /* Worst-case is "\uuu" or result of wctomb() for each char from str */
719     buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
720     b = buf;
721     if (sep[0])
722 #ifndef WINNT_NATIVE
723 	*b++ = sep[0];
724 #else /* WINNT_NATIVE */
725 	*b++ = CHAR & sep[0];
726 #endif /* !WINNT_NATIVE */
727 
728     for (l = 0; l < str->len; l++) {
729 	p = str->buf[l];
730 	if (Iscntrl(p)) {
731 	    *b++ = '^';
732 	    if (p == CTL_ESC('\177'))
733 		*b++ = '?';
734 	    else
735 #ifdef IS_ASCII
736 		*b++ = (unsigned char) (p | 0100);
737 #else
738 		*b++ = _toebcdic[_toascii[p]|0100];
739 #endif
740 	}
741 	else if (p == '^' || p == '\\') {
742 	    *b++ = '\\';
743 	    *b++ = (unsigned char) p;
744 	}
745 	else if (p == ' ' || (Isprint(p) && !Isspace(p)))
746 	    b += one_wctomb((char *)b, p & CHAR);
747 	else {
748 	    *b++ = '\\';
749 	    *b++ = ((p >> 6) & 7) + '0';
750 	    *b++ = ((p >> 3) & 7) + '0';
751 	    *b++ = (p & 7) + '0';
752 	}
753     }
754     if (sep[0] && sep[1])
755 #ifndef WINNT_NATIVE
756 	*b++ = sep[1];
757 #else /* WINNT_NATIVE */
758 	*b++ = CHAR & sep[1];
759 #endif /* !WINNT_NATIVE */
760     *b++ = 0;
761     return buf;			/* should check for overflow */
762 }
763