xref: /freebsd/contrib/tcsh/ed.xmap.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.36 2006/11/29 22:30:09 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.36 2006/11/29 22:30:09 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(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(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
225 	return;
226     }
227 
228     if (Xmap == NULL)
229 	/* tree is initially empty.  Set up new node to match Xkey[0] */
230 	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
231 
232     /* Now recurse through Xmap */
233     (void) TryNode(Xmap, &cs, val, ntype);
234     return;
235 }
236 
237 static int
238 TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
239 {
240     /*
241      * Find a node that matches *string or allocate a new one
242      */
243     if (ptr->ch != *(str->buf)) {
244 	XmapNode *xm;
245 
246 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
247 	    if (xm->sibling->ch == *(str->buf))
248 		break;
249 	if (xm->sibling == NULL)
250 	    xm->sibling = GetFreeNode(str);	/* setup new node */
251 	ptr = xm->sibling;
252     }
253 
254     str->buf++;
255     str->len--;
256     if (str->len == 0) {
257 	size_t len;
258 
259 	/* we're there */
260 	if (ptr->next != NULL) {
261 	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
262 	    ptr->next = NULL;
263 	}
264 
265 	switch (ptr->type) {
266 	case XK_STR:
267 	case XK_EXE:
268 	    xfree(ptr->val.str.buf);
269 	    ptr->val.str.len = 0;
270 	    break;
271 	case XK_NOD:
272 	case XK_CMD:
273 	    break;
274 	default:
275 	    abort();
276 	    break;
277 	}
278 
279 	switch (ptr->type = ntype) {
280 	case XK_CMD:
281 	    ptr->val = *val;
282 	    break;
283 	case XK_STR:
284 	case XK_EXE:
285 	    ptr->val.str.len = val->str.len;
286 	    len = (val->str.len + 1) * sizeof(*ptr->val.str.buf);
287 	    ptr->val.str.buf = xmalloc(len);
288 	    (void) memcpy(ptr->val.str.buf, val->str.buf, len);
289 	    break;
290 	default:
291 	    abort();
292 	    break;
293 	}
294     }
295     else {
296 	/* still more chars to go */
297 	if (ptr->next == NULL)
298 	    ptr->next = GetFreeNode(str);	/* setup new node */
299 	(void) TryNode(ptr->next, str, val, ntype);
300     }
301     return (0);
302 }
303 
304 void
305 ClearXkey(KEYCMD *map, const CStr *in)
306 {
307     unsigned char c = (unsigned char) *(in->buf);
308     if ((map[c] == F_XKEY) &&
309 	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
310 	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
311 	(void) DeleteXkey(in);
312 }
313 
314 int
315 DeleteXkey(const CStr *Xkey)
316 {
317     CStr s;
318 
319     s = *Xkey;
320     if (s.len == 0) {
321 	xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
322 	return (-1);
323     }
324 
325     if (Xmap == NULL)
326 	return (0);
327 
328     (void) TryDeleteNode(&Xmap, &s);
329     return (0);
330 }
331 
332 /* Destroys str */
333 static int
334 TryDeleteNode(XmapNode **inptr, CStr *str)
335 {
336     XmapNode *ptr;
337 
338     ptr = *inptr;
339     /*
340      * Find a node that matches *string or allocate a new one
341      */
342     if (ptr->ch != *(str->buf)) {
343 	XmapNode *xm;
344 
345 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
346 	    if (xm->sibling->ch == *(str->buf))
347 		break;
348 	if (xm->sibling == NULL)
349 	    return (0);
350 	inptr = &xm->sibling;
351 	ptr = xm->sibling;
352     }
353 
354     str->buf++;
355     str->len--;
356 
357     if (str->len == 0) {
358 	/* we're there */
359 	*inptr = ptr->sibling;
360 	ptr->sibling = NULL;
361 	PutFreeNode(ptr);
362 	return (1);
363     }
364     else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
365 	if (ptr->next != NULL)
366 	    return (0);
367 	*inptr = ptr->sibling;
368 	ptr->sibling = NULL;
369 	PutFreeNode(ptr);
370 	return (1);
371     }
372     else {
373 	return (0);
374     }
375 }
376 
377 /* PutFreeNode():
378  *	Puts a tree of nodes onto free list using free(3).
379  */
380 static void
381 PutFreeNode(XmapNode *ptr)
382 {
383     if (ptr == NULL)
384 	return;
385 
386     if (ptr->next != NULL) {
387 	PutFreeNode(ptr->next);
388 	ptr->next = NULL;
389     }
390 
391     PutFreeNode(ptr->sibling);
392 
393     switch (ptr->type) {
394     case XK_CMD:
395     case XK_NOD:
396 	break;
397     case XK_EXE:
398     case XK_STR:
399 	xfree(ptr->val.str.buf);
400 	break;
401     default:
402 	abort();
403 	break;
404     }
405     xfree(ptr);
406 }
407 
408 
409 /* GetFreeNode():
410  *	Returns pointer to an XmapNode for ch.
411  */
412 static XmapNode *
413 GetFreeNode(CStr *ch)
414 {
415     XmapNode *ptr;
416 
417     ptr = xmalloc(sizeof(XmapNode));
418     ptr->ch = ch->buf[0];
419     ptr->type = XK_NOD;
420     ptr->val.str.buf = NULL;
421     ptr->val.str.len = 0;
422     ptr->next = NULL;
423     ptr->sibling = NULL;
424     return (ptr);
425 }
426 
427 
428 /* PrintXKey():
429  *	Print the binding associated with Xkey key.
430  *	Print entire Xmap if null
431  */
432 void
433 PrintXkey(const CStr *key)
434 {
435     struct Strbuf buf = Strbuf_INIT;
436     CStr cs;
437 
438     if (key) {
439 	cs.buf = key->buf;
440 	cs.len = key->len;
441     }
442     else {
443 	cs.buf = STRNULL;
444 	cs.len = 0;
445     }
446     /* do nothing if Xmap is empty and null key specified */
447     if (Xmap == NULL && cs.len == 0)
448 	return;
449 
450     Strbuf_append1(&buf, '"');
451     cleanup_push(&buf, Strbuf_cleanup);
452     if (Lookup(&buf, &cs, Xmap) <= -1)
453 	/* key is not bound */
454 	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
455     cleanup_until(&buf);
456 }
457 
458 /* Lookup():
459  *	look for the string starting at node ptr.
460  *	Print if last node
461  */
462 static int
463 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
464 {
465     if (ptr == NULL)
466 	return (-1);		/* cannot have null ptr */
467 
468     if (str->len == 0) {
469 	/* no more chars in string.  Enumerate from here. */
470 	Enumerate(buf, ptr);
471 	return (0);
472     }
473     else {
474 	/* If match put this char into buf.  Recurse */
475 	if (ptr->ch == *(str->buf)) {
476 	    /* match found */
477 	    unparsech(buf, ptr->ch);
478 	    if (ptr->next != NULL) {
479 		/* not yet at leaf */
480 		CStr tstr;
481 		tstr.buf = str->buf + 1;
482 		tstr.len = str->len - 1;
483 		return (Lookup(buf, &tstr, ptr->next));
484 	    }
485 	    else {
486 		/* next node is null so key should be complete */
487 		if (str->len == 1) {
488 		    Strbuf_append1(buf, '"');
489 		    Strbuf_terminate(buf);
490 		    printOne(buf->s, &ptr->val, ptr->type);
491 		    return (0);
492 		}
493 		else
494 		    return (-1);/* mismatch -- string still has chars */
495 	    }
496 	}
497 	else {
498 	    /* no match found try sibling */
499 	    if (ptr->sibling)
500 		return (Lookup(buf, str, ptr->sibling));
501 	    else
502 		return (-1);
503 	}
504     }
505 }
506 
507 static void
508 Enumerate(struct Strbuf *buf, const XmapNode *ptr)
509 {
510     size_t old_len;
511 
512     if (ptr == NULL) {
513 #ifdef DEBUG_EDIT
514 	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
515 #endif
516 	return;
517     }
518 
519     old_len = buf->len;
520     unparsech(buf, ptr->ch); /* put this char at end of string */
521     if (ptr->next == NULL) {
522 	/* print this Xkey and function */
523 	Strbuf_append1(buf, '"');
524 	Strbuf_terminate(buf);
525 	printOne(buf->s, &ptr->val, ptr->type);
526     }
527     else
528 	Enumerate(buf, ptr->next);
529 
530     /* go to sibling if there is one */
531     if (ptr->sibling) {
532 	buf->len = old_len;
533 	Enumerate(buf, ptr->sibling);
534     }
535 }
536 
537 
538 /* PrintOne():
539  *	Print the specified key and its associated
540  *	function specified by val
541  */
542 void
543 printOne(const Char *key, const XmapVal *val, int ntype)
544 {
545     struct KeyFuncs *fp;
546     static const char *fmt = "%s\n";
547 
548     xprintf("%-15S-> ", key);
549     if (val != NULL)
550 	switch (ntype) {
551 	case XK_STR:
552 	case XK_EXE: {
553 	    unsigned char *p;
554 
555 	    p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
556 	    cleanup_push(p, xfree);
557 	    xprintf(fmt, p);
558 	    cleanup_until(p);
559 	    break;
560 	}
561 	case XK_CMD:
562 	    for (fp = FuncNames; fp->name; fp++)
563 		if (val->cmd == fp->func)
564 		    xprintf(fmt, fp->name);
565 		break;
566 	default:
567 	    abort();
568 	    break;
569 	}
570     else
571 	xprintf(fmt, CGETS(9, 7, "no input"));
572 }
573 
574 static void
575 unparsech(struct Strbuf *buf, Char ch)
576 {
577     if (ch == 0) {
578 	Strbuf_append1(buf, '^');
579 	Strbuf_append1(buf, '@');
580     }
581     else if (Iscntrl(ch)) {
582 	Strbuf_append1(buf, '^');
583 	if (ch == CTL_ESC('\177'))
584 	    Strbuf_append1(buf, '?');
585 	else
586 #ifdef IS_ASCII
587 	    Strbuf_append1(buf, ch | 0100);
588 #else
589 	    Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
590 #endif
591     }
592     else if (ch == '^') {
593 	Strbuf_append1(buf, '\\');
594 	Strbuf_append1(buf, '^');
595     } else if (ch == '\\') {
596 	Strbuf_append1(buf, '\\');
597 	Strbuf_append1(buf, '\\');
598     } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
599 	Strbuf_append1(buf, ch);
600     }
601     else {
602 	Strbuf_append1(buf, '\\');
603 	Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
604 	Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
605 	Strbuf_append1(buf, (ch & 7) + '0');
606     }
607 }
608 
609 eChar
610 parseescape(const Char **ptr)
611 {
612     const Char *p;
613     Char c;
614 
615     p = *ptr;
616 
617     if ((p[1] & CHAR) == 0) {
618 	xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
619 	return CHAR_ERR;
620     }
621     if ((*p & CHAR) == '\\') {
622 	p++;
623 	switch (*p & CHAR) {
624 	case 'a':
625 	    c = CTL_ESC('\007');         /* Bell */
626 	    break;
627 	case 'b':
628 	    c = CTL_ESC('\010');         /* Backspace */
629 	    break;
630 	case 'e':
631 	    c = CTL_ESC('\033');         /* Escape */
632 	    break;
633 	case 'f':
634 	    c = CTL_ESC('\014');         /* Form Feed */
635 	    break;
636 	case 'n':
637 	    c = CTL_ESC('\012');         /* New Line */
638 	    break;
639 	case 'r':
640 	    c = CTL_ESC('\015');         /* Carriage Return */
641 	    break;
642 	case 't':
643 	    c = CTL_ESC('\011');         /* Horizontal Tab */
644 	    break;
645 	case 'v':
646 	    c = CTL_ESC('\013');         /* Vertical Tab */
647 	    break;
648 	case '\\':
649 	    c = '\\';
650 	    break;
651 	case '0':
652 	case '1':
653 	case '2':
654 	case '3':
655 	case '4':
656 	case '5':
657 	case '6':
658 	case '7':
659 	    {
660 		int cnt, val;
661 		Char ch;
662 
663 		for (cnt = 0, val = 0; cnt < 3; cnt++) {
664 		    ch = *p++ & CHAR;
665 		    if (ch < '0' || ch > '7') {
666 			p--;
667 			break;
668 		    }
669 		    val = (val << 3) | (ch - '0');
670 		}
671 		if ((val & ~0xff) != 0) {
672 		    xprintf(CGETS(9, 9,
673 			    "Octal constant does not fit in a char.\n"));
674 		    return 0;
675 		}
676 #ifndef IS_ASCII
677 		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
678 		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
679 			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
680 #endif
681 		c = (Char) val;
682 		--p;
683 	    }
684 	    break;
685 	default:
686 	    c = *p;
687 	    break;
688 	}
689     }
690     else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
691 				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
692 	p++;
693 #ifdef IS_ASCII
694 	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
695 #else
696 	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
697 	if (adrof(STRwarnebcdic))
698 	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
699 		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
700 #endif
701     }
702     else
703 	c = *p;
704     *ptr = p;
705     return (c);
706 }
707 
708 
709 unsigned char *
710 unparsestring(const CStr *str, const Char *sep)
711 {
712     unsigned char *buf, *b;
713     Char   p;
714     int l;
715 
716     /* Worst-case is "\uuu" or result of wctomb() for each char from str */
717     buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
718     b = buf;
719     if (sep[0])
720 #ifndef WINNT_NATIVE
721 	*b++ = sep[0];
722 #else /* WINNT_NATIVE */
723 	*b++ = CHAR & sep[0];
724 #endif /* !WINNT_NATIVE */
725 
726     for (l = 0; l < str->len; l++) {
727 	p = str->buf[l];
728 	if (Iscntrl(p)) {
729 	    *b++ = '^';
730 	    if (p == CTL_ESC('\177'))
731 		*b++ = '?';
732 	    else
733 #ifdef IS_ASCII
734 		*b++ = (unsigned char) (p | 0100);
735 #else
736 		*b++ = _toebcdic[_toascii[p]|0100];
737 #endif
738 	}
739 	else if (p == '^' || p == '\\') {
740 	    *b++ = '\\';
741 	    *b++ = (unsigned char) p;
742 	}
743 	else if (p == ' ' || (Isprint(p) && !Isspace(p)))
744 	    b += one_wctomb((char *)b, p & CHAR);
745 	else {
746 	    *b++ = '\\';
747 	    *b++ = ((p >> 6) & 7) + '0';
748 	    *b++ = ((p >> 3) & 7) + '0';
749 	    *b++ = (p & 7) + '0';
750 	}
751     }
752     if (sep[0] && sep[1])
753 #ifndef WINNT_NATIVE
754 	*b++ = sep[1];
755 #else /* WINNT_NATIVE */
756 	*b++ = CHAR & sep[1];
757 #endif /* !WINNT_NATIVE */
758     *b++ = 0;
759     return buf;			/* should check for overflow */
760 }
761