xref: /freebsd/contrib/tcsh/ed.xmap.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
1 /* $Header: /src/pub/tcsh/ed.xmap.c,v 3.28 2005/01/05 18:06:43 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("$Id: ed.xmap.c,v 3.28 2005/01/05 18:06:43 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 #define MAXXKEY 100		/* max length of a Xkey for print putposes */
116 static Char printbuf[MAXXKEY];	/* buffer for printing */
117 
118 
119 /* Some declarations of procedures */
120 static	int       TraverseMap	__P((XmapNode *, CStr *, XmapVal *));
121 static	int       TryNode	__P((XmapNode *, CStr *, XmapVal *, int));
122 static	XmapNode *GetFreeNode	__P((CStr *));
123 static	void	  PutFreeNode	__P((XmapNode *));
124 static	int	  TryDeleteNode	__P((XmapNode **, CStr *));
125 static	int	  Lookup	__P((CStr *, XmapNode *, int));
126 static	int	  Enumerate	__P((XmapNode *, int));
127 static	int	  unparsech	__P((int, Char *));
128 
129 
130 XmapVal *
131 XmapCmd(cmd)
132     int cmd;
133 {
134     static XmapVal xm;
135     xm.cmd = (KEYCMD) cmd;
136     return &xm;
137 }
138 
139 XmapVal *
140 XmapStr(str)
141     CStr  *str;
142 {
143     static XmapVal xm;
144     xm.str.len = str->len;
145     xm.str.buf = str->buf;
146     return &xm;
147 }
148 
149 /* ResetXmap():
150  *	Takes all nodes on Xmap and puts them on free list.  Then
151  *	initializes Xmap with arrow keys
152  */
153 void
154 ResetXmap()
155 {
156     PutFreeNode(Xmap);
157     Xmap = NULL;
158 
159     DefaultArrowKeys();
160     return;
161 }
162 
163 
164 /* GetXkey():
165  *	Calls the recursive function with entry point Xmap
166  */
167 int
168 GetXkey(ch, val)
169     CStr     *ch;
170     XmapVal  *val;
171 {
172     return (TraverseMap(Xmap, ch, val));
173 }
174 
175 /* TraverseMap():
176  *	recursively traverses node in tree until match or mismatch is
177  * 	found.  May read in more characters.
178  */
179 static int
180 TraverseMap(ptr, ch, val)
181     XmapNode *ptr;
182     CStr     *ch;
183     XmapVal  *val;
184 {
185     Char    tch;
186 
187     if (ptr->ch == *(ch->buf)) {
188 	/* match found */
189 	if (ptr->next) {
190 	    /* Xkey not complete so get next char */
191 	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
192 		val->cmd = F_SEND_EOF;
193 		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
194 	    }
195 	    *(ch->buf) = tch;
196 	    return (TraverseMap(ptr->next, ch, val));
197 	}
198 	else {
199 	    *val = ptr->val;
200 	    if (ptr->type != XK_CMD)
201 		*(ch->buf) = '\0';
202 	    return ptr->type;
203 	}
204     }
205     else {
206 	/* no match found here */
207 	if (ptr->sibling) {
208 	    /* try next sibling */
209 	    return (TraverseMap(ptr->sibling, ch, val));
210 	}
211 	else {
212 	    /* no next sibling -- mismatch */
213 	    val->str.buf = NULL;
214 	    val->str.len = 0;
215 	    return XK_STR;
216 	}
217     }
218 }
219 
220 void
221 AddXkey(Xkey, val, ntype)
222     CStr    *Xkey;
223     XmapVal *val;
224     int      ntype;
225 {
226     CStr cs;
227     cs.buf = Xkey->buf;
228     cs.len = Xkey->len;
229     if (Xkey->len == 0) {
230 	xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
231 	return;
232     }
233 
234     if (ntype == XK_CMD && val->cmd == F_XKEY) {
235 	xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
236 	return;
237     }
238 
239     if (Xmap == NULL)
240 	/* tree is initially empty.  Set up new node to match Xkey[0] */
241 	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
242 
243     /* Now recurse through Xmap */
244     (void) TryNode(Xmap, &cs, val, ntype);
245     return;
246 }
247 
248 static int
249 TryNode(ptr, str, val, ntype)
250     XmapNode *ptr;
251     CStr     *str;
252     XmapVal  *val;
253     int       ntype;
254 {
255     /*
256      * Find a node that matches *string or allocate a new one
257      */
258     if (ptr->ch != *(str->buf)) {
259 	XmapNode *xm;
260 
261 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
262 	    if (xm->sibling->ch == *(str->buf))
263 		break;
264 	if (xm->sibling == NULL)
265 	    xm->sibling = GetFreeNode(str);	/* setup new node */
266 	ptr = xm->sibling;
267     }
268 
269     str->buf++;
270     str->len--;
271     if (str->len == 0) {
272 	/* we're there */
273 	if (ptr->next != NULL) {
274 	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
275 	    ptr->next = NULL;
276 	}
277 
278 	switch (ptr->type) {
279 	case XK_STR:
280 	case XK_EXE:
281 	    if (ptr->val.str.buf != NULL)
282 		xfree((ptr_t) ptr->val.str.buf);
283 	    ptr->val.str.len = 0;
284 	    break;
285 	case XK_NOD:
286 	case XK_CMD:
287 	    break;
288 	default:
289 	    abort();
290 	    break;
291 	}
292 
293 	switch (ptr->type = ntype) {
294 	case XK_CMD:
295 	    ptr->val = *val;
296 	    break;
297 	case XK_STR:
298 	case XK_EXE:
299 	    ptr->val.str.len = (val->str.len + 1) * sizeof(Char);
300 	    ptr->val.str.buf = (Char *) xmalloc((size_t) ptr->val.str.len);
301 	    (void) memmove((ptr_t) ptr->val.str.buf, (ptr_t) val->str.buf,
302 			   (size_t) ptr->val.str.len);
303 	    ptr->val.str.len = val->str.len;
304 	    break;
305 	default:
306 	    abort();
307 	    break;
308 	}
309     }
310     else {
311 	/* still more chars to go */
312 	if (ptr->next == NULL)
313 	    ptr->next = GetFreeNode(str);	/* setup new node */
314 	(void) TryNode(ptr->next, str, val, ntype);
315     }
316     return (0);
317 }
318 
319 void
320 ClearXkey(map, in)
321     KEYCMD *map;
322     CStr   *in;
323 {
324     unsigned char c = (unsigned char) *(in->buf);
325     if ((map[c] == F_XKEY) &&
326 	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
327 	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
328 	(void) DeleteXkey(in);
329 }
330 
331 int
332 DeleteXkey(Xkey)
333     CStr   *Xkey;
334 {
335     if (Xkey->len == 0) {
336 	xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
337 	return (-1);
338     }
339 
340     if (Xmap == NULL)
341 	return (0);
342 
343     (void) TryDeleteNode(&Xmap, Xkey);
344     return (0);
345 }
346 
347 static int
348 TryDeleteNode(inptr, str)
349     XmapNode **inptr;
350     CStr   *str;
351 {
352     XmapNode *ptr;
353     XmapNode *prev_ptr = NULL;
354 
355     ptr = *inptr;
356     /*
357      * Find a node that matches *string or allocate a new one
358      */
359     if (ptr->ch != *(str->buf)) {
360 	XmapNode *xm;
361 
362 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
363 	    if (xm->sibling->ch == *(str->buf))
364 		break;
365 	if (xm->sibling == NULL)
366 	    return (0);
367 	prev_ptr = xm;
368 	ptr = xm->sibling;
369     }
370 
371     str->buf++;
372     str->len--;
373 
374     if (str->len == 0) {
375 	/* we're there */
376 	if (prev_ptr == NULL)
377 	    *inptr = ptr->sibling;
378 	else
379 	    prev_ptr->sibling = ptr->sibling;
380 	ptr->sibling = NULL;
381 	PutFreeNode(ptr);
382 	return (1);
383     }
384     else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
385 	if (ptr->next != NULL)
386 	    return (0);
387 	if (prev_ptr == NULL)
388 	    *inptr = ptr->sibling;
389 	else
390 	    prev_ptr->sibling = ptr->sibling;
391 	ptr->sibling = NULL;
392 	PutFreeNode(ptr);
393 	return (1);
394     }
395     else {
396 	return (0);
397     }
398 }
399 
400 /* PutFreeNode():
401  *	Puts a tree of nodes onto free list using free(3).
402  */
403 static void
404 PutFreeNode(ptr)
405     XmapNode *ptr;
406 {
407     if (ptr == NULL)
408 	return;
409 
410     if (ptr->next != NULL) {
411 	PutFreeNode(ptr->next);
412 	ptr->next = NULL;
413     }
414 
415     PutFreeNode(ptr->sibling);
416 
417     switch (ptr->type) {
418     case XK_CMD:
419     case XK_NOD:
420 	break;
421     case XK_EXE:
422     case XK_STR:
423 	if (ptr->val.str.buf != NULL)
424 	    xfree((ptr_t) ptr->val.str.buf);
425 	break;
426     default:
427 	abort();
428 	break;
429     }
430     xfree((ptr_t) ptr);
431 }
432 
433 
434 /* GetFreeNode():
435  *	Returns pointer to an XmapNode for ch.
436  */
437 static XmapNode *
438 GetFreeNode(ch)
439     CStr *ch;
440 {
441     XmapNode *ptr;
442 
443     ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode));
444     ptr->ch = ch->buf[0];
445     ptr->type = XK_NOD;
446     ptr->val.str.buf = NULL;
447     ptr->val.str.len = 0;
448     ptr->next = NULL;
449     ptr->sibling = NULL;
450     return (ptr);
451 }
452 
453 
454 /* PrintXKey():
455  *	Print the binding associated with Xkey key.
456  *	Print entire Xmap if null
457  */
458 void
459 PrintXkey(key)
460     CStr   *key;
461 {
462     CStr cs;
463 
464     if (key) {
465 	cs.buf = key->buf;
466 	cs.len = key->len;
467     }
468     else {
469 	cs.buf = STRNULL;
470 	cs.len = 0;
471     }
472     /* do nothing if Xmap is empty and null key specified */
473     if (Xmap == NULL && cs.len == 0)
474 	return;
475 
476     printbuf[0] =  '"';
477     if (Lookup(&cs, Xmap, 1) <= -1)
478 	/* key is not bound */
479 	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
480     return;
481 }
482 
483 /* Lookup():
484  *	look for the string starting at node ptr.
485  *	Print if last node
486  */
487 static int
488 Lookup(str, ptr, cnt)
489     CStr   *str;
490     XmapNode *ptr;
491     int     cnt;
492 {
493     int     ncnt;
494 
495     if (ptr == NULL)
496 	return (-1);		/* cannot have null ptr */
497 
498     if (str->len == 0) {
499 	/* no more chars in string.  Enumerate from here. */
500 	(void) Enumerate(ptr, cnt);
501 	return (0);
502     }
503     else {
504 	/* If match put this char into printbuf.  Recurse */
505 	if (ptr->ch == *(str->buf)) {
506 	    /* match found */
507 	    ncnt = unparsech(cnt, &ptr->ch);
508 	    if (ptr->next != NULL) {
509 		/* not yet at leaf */
510 		CStr tstr;
511 		tstr.buf = str->buf + 1;
512 		tstr.len = str->len - 1;
513 		return (Lookup(&tstr, ptr->next, ncnt + 1));
514 	    }
515 	    else {
516 		/* next node is null so key should be complete */
517 		if (str->len == 1) {
518 		    CStr pb;
519 		    printbuf[ncnt + 1] = '"';
520 		    printbuf[ncnt + 2] = '\0';
521 		    pb.buf = printbuf;
522 		    pb.len = ncnt + 2;
523 		    (void) printOne(&pb, &ptr->val, ptr->type);
524 		    return (0);
525 		}
526 		else
527 		    return (-1);/* mismatch -- string still has chars */
528 	    }
529 	}
530 	else {
531 	    /* no match found try sibling */
532 	    if (ptr->sibling)
533 		return (Lookup(str, ptr->sibling, cnt));
534 	    else
535 		return (-1);
536 	}
537     }
538 }
539 
540 static int
541 Enumerate(ptr, cnt)
542     XmapNode *ptr;
543     int     cnt;
544 {
545     int     ncnt;
546 
547     if (cnt >= MAXXKEY - 5) {	/* buffer too small */
548 	printbuf[++cnt] = '"';
549 	printbuf[++cnt] = '\0';
550 	xprintf(CGETS(9, 5,
551 		"Some extended keys too long for internal print buffer"));
552 	xprintf(" \"%S...\"\n", printbuf);
553 	return (0);
554     }
555 
556     if (ptr == NULL) {
557 #ifdef DEBUG_EDIT
558 	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
559 #endif
560 	return (-1);
561     }
562 
563     ncnt = unparsech(cnt, &ptr->ch); /* put this char at end of string */
564     if (ptr->next == NULL) {
565 	CStr pb;
566 	/* print this Xkey and function */
567 	printbuf[++ncnt] = '"';
568 	printbuf[++ncnt] = '\0';
569 	pb.buf = printbuf;
570 	pb.len = ncnt;
571 	(void) printOne(&pb, &ptr->val, ptr->type);
572     }
573     else
574 	(void) Enumerate(ptr->next, ncnt + 1);
575 
576     /* go to sibling if there is one */
577     if (ptr->sibling)
578 	(void) Enumerate(ptr->sibling, cnt);
579     return (0);
580 }
581 
582 
583 /* PrintOne():
584  *	Print the specified key and its associated
585  *	function specified by val
586  */
587 int
588 printOne(key, val, ntype)
589     CStr    *key;
590     XmapVal *val;
591     int      ntype;
592 {
593     struct KeyFuncs *fp;
594     unsigned char unparsbuf[200];
595     static const char *fmt = "%s\n";
596 
597     xprintf("%-15S-> ", key->buf);
598     if (val != NULL)
599 	switch (ntype) {
600 	case XK_STR:
601 	case XK_EXE:
602 	    xprintf(fmt, unparsestring(&val->str, unparsbuf,
603 				       ntype == XK_STR ? STRQQ : STRBB));
604 	    break;
605 	case XK_CMD:
606 	    for (fp = FuncNames; fp->name; fp++)
607 		if (val->cmd == fp->func)
608 		    xprintf(fmt, fp->name);
609 		break;
610 	default:
611 	    abort();
612 	    break;
613 	}
614     else
615 	xprintf(fmt, key, CGETS(9, 7, "no input"));
616     return (0);
617 }
618 
619 static int
620 unparsech(cnt, ch)
621     int   cnt;
622     Char  *ch;
623 {
624     if (ch == 0) {
625 	printbuf[cnt++] = '^';
626 	printbuf[cnt] = '@';
627 	return cnt;
628     }
629 
630     if (Iscntrl(*ch)) {
631 #ifdef IS_ASCII
632 	printbuf[cnt++] = '^';
633 	if (*ch == CTL_ESC('\177'))
634 	    printbuf[cnt] = '?';
635 	else
636 	    printbuf[cnt] = *ch | 0100;
637 #else
638 	if (*ch == CTL_ESC('\177'))
639 	{
640 		printbuf[cnt++] = '^';
641 		printbuf[cnt] = '?';
642 	}
643 	else if (Isupper(_toebcdic[_toascii[*ch]|0100])
644 		|| strchr("@[\\]^_", _toebcdic[_toascii[*ch]|0100]) != NULL)
645 	{
646 		printbuf[cnt++] = '^';
647 		printbuf[cnt] = _toebcdic[_toascii[*ch]|0100];
648 	}
649 	else
650 	{
651 		printbuf[cnt++] = '\\';
652 		printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
653 		printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
654 		printbuf[cnt] = (*ch & 7) + '0';
655 	}
656 #endif
657     }
658     else if (*ch == '^') {
659 	printbuf[cnt++] = '\\';
660 	printbuf[cnt] = '^';
661     }
662     else if (*ch == '\\') {
663 	printbuf[cnt++] = '\\';
664 	printbuf[cnt] = '\\';
665     }
666     else if (*ch == ' ' || (Isprint(*ch) && !Isspace(*ch))) {
667 	printbuf[cnt] = *ch;
668     }
669     else {
670 	printbuf[cnt++] = '\\';
671 	printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
672 	printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
673 	printbuf[cnt] = (*ch & 7) + '0';
674     }
675     return cnt;
676 }
677 
678 eChar
679 parseescape(ptr)
680     const Char  **ptr;
681 {
682     const Char *p;
683     Char c;
684 
685     p = *ptr;
686 
687     if ((p[1] & CHAR) == 0) {
688 	xprintf(CGETS(9, 8, "Something must follow: %c\n"), *p);
689 	return CHAR_ERR;
690     }
691     if ((*p & CHAR) == '\\') {
692 	p++;
693 	switch (*p & CHAR) {
694 	case 'a':
695 	    c = CTL_ESC('\007');         /* Bell */
696 	    break;
697 	case 'b':
698 	    c = CTL_ESC('\010');         /* Backspace */
699 	    break;
700 	case 'e':
701 	    c = CTL_ESC('\033');         /* Escape */
702 	    break;
703 	case 'f':
704 	    c = CTL_ESC('\014');         /* Form Feed */
705 	    break;
706 	case 'n':
707 	    c = CTL_ESC('\012');         /* New Line */
708 	    break;
709 	case 'r':
710 	    c = CTL_ESC('\015');         /* Carriage Return */
711 	    break;
712 	case 't':
713 	    c = CTL_ESC('\011');         /* Horizontal Tab */
714 	    break;
715 	case 'v':
716 	    c = CTL_ESC('\013');         /* Vertical Tab */
717 	    break;
718 	case '\\':
719 	    c = '\\';
720 	    break;
721 	case '0':
722 	case '1':
723 	case '2':
724 	case '3':
725 	case '4':
726 	case '5':
727 	case '6':
728 	case '7':
729 	    {
730 		int cnt, val;
731 		Char ch;
732 
733 		for (cnt = 0, val = 0; cnt < 3; cnt++) {
734 		    ch = *p++ & CHAR;
735 		    if (ch < '0' || ch > '7') {
736 			p--;
737 			break;
738 		    }
739 		    val = (val << 3) | (ch - '0');
740 		}
741 		if ((val & 0xffffff00) != 0) {
742 		    xprintf(CGETS(9, 9,
743 			    "Octal constant does not fit in a char.\n"));
744 		    return 0;
745 		}
746 #ifndef IS_ASCII
747 		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
748 		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
749 			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
750 #endif
751 		c = (Char) val;
752 		--p;
753 	    }
754 	    break;
755 	default:
756 	    c = *p;
757 	    break;
758 	}
759     }
760     else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
761 				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
762 	p++;
763 #ifdef IS_ASCII
764 	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
765 #else
766 	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
767 	if (adrof(STRwarnebcdic))
768 	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
769 		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
770 #endif
771     }
772     else
773 	c = *p;
774     *ptr = p;
775     return (c);
776 }
777 
778 
779 unsigned char *
780 unparsestring(str, buf, sep)
781     CStr   *str;
782     unsigned char *buf;
783     Char   *sep;
784 {
785     unsigned char *b;
786     Char   p;
787     int l;
788 
789     b = buf;
790     if (sep[0])
791 #ifndef WINNT_NATIVE
792 	*b++ = sep[0];
793 #else /* WINNT_NATIVE */
794 	*b++ = CHAR & sep[0];
795 #endif /* !WINNT_NATIVE */
796 
797     for (l = 0; l < str->len; l++) {
798 	p = str->buf[l];
799 	if (Iscntrl(p)) {
800 #ifdef IS_ASCII
801 	    *b++ = '^';
802 	    if (p == CTL_ESC('\177'))
803 		*b++ = '?';
804 	    else
805 		*b++ = (unsigned char) (p | 0100);
806 #else
807 	    if (_toascii[p] == '\177' || Isupper(_toebcdic[_toascii[p]|0100])
808 		 || strchr("@[\\]^_", _toebcdic[_toascii[p]|0100]) != NULL)
809 	    {
810 		*b++ = '^';
811 		*b++ = (_toascii[p] == '\177') ? '?' : _toebcdic[_toascii[p]|0100];
812 	    }
813 	    else
814 	    {
815 		*b++ = '\\';
816 		*b++ = ((p >> 6) & 7) + '0';
817 		*b++ = ((p >> 3) & 7) + '0';
818 		*b++ = (p & 7) + '0';
819 	    }
820 #endif
821 	}
822 	else if (p == '^' || p == '\\') {
823 	    *b++ = '\\';
824 	    *b++ = (unsigned char) p;
825 	}
826 	else if (p == ' ' || (Isprint(p) && !Isspace(p)))
827 	    b += one_wctomb((char *)b, p & CHAR);
828 	else {
829 	    *b++ = '\\';
830 	    *b++ = ((p >> 6) & 7) + '0';
831 	    *b++ = ((p >> 3) & 7) + '0';
832 	    *b++ = (p & 7) + '0';
833 	}
834     }
835     if (sep[0] && sep[1])
836 #ifndef WINNT_NATIVE
837 	*b++ = sep[1];
838 #else /* WINNT_NATIVE */
839 	*b++ = CHAR & sep[1];
840 #endif /* !WINNT_NATIVE */
841     *b++ = 0;
842     return buf;			/* should check for overflow */
843 }
844