xref: /freebsd/contrib/tcsh/ed.xmap.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
1 /* $Header: /src/pub/tcsh/ed.xmap.c,v 3.23 2000/11/11 23:03:35 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. All advertising materials mentioning features or use of this software
74  *    must display the following acknowledgement:
75  *	This product includes software developed by the University of
76  *	California, Berkeley and its contributors.
77  * 4. Neither the name of the University nor the names of its contributors
78  *    may be used to endorse or promote products derived from this software
79  *    without specific prior written permission.
80  *
81  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
82  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
83  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
84  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
85  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
86  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
87  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
88  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
89  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
90  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
91  * SUCH DAMAGE.
92  */
93 #include "sh.h"
94 
95 RCSID("$Id: ed.xmap.c,v 3.23 2000/11/11 23:03:35 christos Exp $")
96 
97 #include "ed.h"
98 #include "ed.defns.h"
99 
100 #ifndef NULL
101 #define NULL 0
102 #endif
103 
104 /* Internal Data types and declarations */
105 
106 /* The Nodes of the Xmap.  The Xmap is a linked list of these node
107  * elements
108  */
109 typedef struct Xmapnode {
110     Char    ch;			/* single character of Xkey */
111     int     type;
112     XmapVal val; 		/* command code or pointer to string, if this
113 				 * is a leaf */
114     struct Xmapnode *next;	/* ptr to next char of this Xkey */
115     struct Xmapnode *sibling;	/* ptr to another Xkey with same prefix */
116 } XmapNode;
117 
118 static XmapNode *Xmap = NULL;	/* the current Xmap */
119 #define MAXXKEY 100		/* max length of a Xkey for print putposes */
120 static Char printbuf[MAXXKEY];	/* buffer for printing */
121 
122 
123 /* Some declarations of procedures */
124 static	int       TraverseMap	__P((XmapNode *, CStr *, XmapVal *));
125 static	int       TryNode	__P((XmapNode *, CStr *, XmapVal *, int));
126 static	XmapNode *GetFreeNode	__P((CStr *));
127 static	void	  PutFreeNode	__P((XmapNode *));
128 static	int	  TryDeleteNode	__P((XmapNode **, CStr *));
129 static	int	  Lookup	__P((CStr *, XmapNode *, int));
130 static	int	  Enumerate	__P((XmapNode *, int));
131 static	int	  unparsech	__P((int, Char *));
132 
133 
134 XmapVal *
135 XmapCmd(cmd)
136     int cmd;
137 {
138     static XmapVal xm;
139     xm.cmd = (KEYCMD) cmd;
140     return &xm;
141 }
142 
143 XmapVal *
144 XmapStr(str)
145     CStr  *str;
146 {
147     static XmapVal xm;
148     xm.str.len = str->len;
149     xm.str.buf = str->buf;
150     return &xm;
151 }
152 
153 /* ResetXmap():
154  *	Takes all nodes on Xmap and puts them on free list.  Then
155  *	initializes Xmap with arrow keys
156  */
157 void
158 ResetXmap()
159 {
160     PutFreeNode(Xmap);
161     Xmap = NULL;
162 
163     DefaultArrowKeys();
164     return;
165 }
166 
167 
168 /* GetXkey():
169  *	Calls the recursive function with entry point Xmap
170  */
171 int
172 GetXkey(ch, val)
173     CStr     *ch;
174     XmapVal  *val;
175 {
176     return (TraverseMap(Xmap, ch, val));
177 }
178 
179 /* TraverseMap():
180  *	recursively traverses node in tree until match or mismatch is
181  * 	found.  May read in more characters.
182  */
183 static int
184 TraverseMap(ptr, ch, val)
185     XmapNode *ptr;
186     CStr     *ch;
187     XmapVal  *val;
188 {
189     Char    tch;
190 
191     if (ptr->ch == *(ch->buf)) {
192 	/* match found */
193 	if (ptr->next) {
194 	    /* Xkey not complete so get next char */
195 	    if (GetNextChar(&tch) != 1) {	/* if EOF or error */
196 		val->cmd = F_SEND_EOF;
197 		return XK_CMD;/* PWP: Pretend we just read an end-of-file */
198 	    }
199 	    *(ch->buf) = tch;
200 	    return (TraverseMap(ptr->next, ch, val));
201 	}
202 	else {
203 	    *val = ptr->val;
204 	    if (ptr->type != XK_CMD)
205 		*(ch->buf) = '\0';
206 	    return ptr->type;
207 	}
208     }
209     else {
210 	/* no match found here */
211 	if (ptr->sibling) {
212 	    /* try next sibling */
213 	    return (TraverseMap(ptr->sibling, ch, val));
214 	}
215 	else {
216 	    /* no next sibling -- mismatch */
217 	    val->str.buf = NULL;
218 	    val->str.len = 0;
219 	    return XK_STR;
220 	}
221     }
222 }
223 
224 void
225 AddXkey(Xkey, val, ntype)
226     CStr    *Xkey;
227     XmapVal *val;
228     int      ntype;
229 {
230     CStr cs;
231     cs.buf = Xkey->buf;
232     cs.len = Xkey->len;
233     if (Xkey->len == 0) {
234 	xprintf(CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
235 	return;
236     }
237 
238     if (ntype == XK_CMD && val->cmd == F_XKEY) {
239 	xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
240 	return;
241     }
242 
243     if (Xmap == NULL)
244 	/* tree is initially empty.  Set up new node to match Xkey[0] */
245 	Xmap = GetFreeNode(&cs);	/* it is properly initialized */
246 
247     /* Now recurse through Xmap */
248     (void) TryNode(Xmap, &cs, val, ntype);
249     return;
250 }
251 
252 static int
253 TryNode(ptr, str, val, ntype)
254     XmapNode *ptr;
255     CStr     *str;
256     XmapVal  *val;
257     int       ntype;
258 {
259     /*
260      * Find a node that matches *string or allocate a new one
261      */
262     if (ptr->ch != *(str->buf)) {
263 	XmapNode *xm;
264 
265 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
266 	    if (xm->sibling->ch == *(str->buf))
267 		break;
268 	if (xm->sibling == NULL)
269 	    xm->sibling = GetFreeNode(str);	/* setup new node */
270 	ptr = xm->sibling;
271     }
272 
273     str->buf++;
274     str->len--;
275     if (str->len == 0) {
276 	/* we're there */
277 	if (ptr->next != NULL) {
278 	    PutFreeNode(ptr->next);	/* lose longer Xkeys with this prefix */
279 	    ptr->next = NULL;
280 	}
281 
282 	switch (ptr->type) {
283 	case XK_STR:
284 	case XK_EXE:
285 	    if (ptr->val.str.buf != NULL)
286 		xfree((ptr_t) ptr->val.str.buf);
287 	    ptr->val.str.len = 0;
288 	    break;
289 	case XK_NOD:
290 	case XK_CMD:
291 	    break;
292 	default:
293 	    abort();
294 	    break;
295 	}
296 
297 	switch (ptr->type = ntype) {
298 	case XK_CMD:
299 	    ptr->val = *val;
300 	    break;
301 	case XK_STR:
302 	case XK_EXE:
303 	    ptr->val.str.len = (val->str.len + 1) * sizeof(Char);
304 	    ptr->val.str.buf = (Char *) xmalloc((size_t) ptr->val.str.len);
305 	    (void) memmove((ptr_t) ptr->val.str.buf, (ptr_t) val->str.buf,
306 			   (size_t) ptr->val.str.len);
307 	    ptr->val.str.len = val->str.len;
308 	    break;
309 	default:
310 	    abort();
311 	    break;
312 	}
313     }
314     else {
315 	/* still more chars to go */
316 	if (ptr->next == NULL)
317 	    ptr->next = GetFreeNode(str);	/* setup new node */
318 	(void) TryNode(ptr->next, str, val, ntype);
319     }
320     return (0);
321 }
322 
323 void
324 ClearXkey(map, in)
325     KEYCMD *map;
326     CStr   *in;
327 {
328     unsigned char c = (unsigned char) *(in->buf);
329     if ((map[c] == F_XKEY) &&
330 	((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
331 	 (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
332 	(void) DeleteXkey(in);
333 }
334 
335 int
336 DeleteXkey(Xkey)
337     CStr   *Xkey;
338 {
339     if (Xkey->len == 0) {
340 	xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
341 	return (-1);
342     }
343 
344     if (Xmap == NULL)
345 	return (0);
346 
347     (void) TryDeleteNode(&Xmap, Xkey);
348     return (0);
349 }
350 
351 static int
352 TryDeleteNode(inptr, str)
353     XmapNode **inptr;
354     CStr   *str;
355 {
356     XmapNode *ptr;
357     XmapNode *prev_ptr = NULL;
358 
359     ptr = *inptr;
360     /*
361      * Find a node that matches *string or allocate a new one
362      */
363     if (ptr->ch != *(str->buf)) {
364 	XmapNode *xm;
365 
366 	for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
367 	    if (xm->sibling->ch == *(str->buf))
368 		break;
369 	if (xm->sibling == NULL)
370 	    return (0);
371 	prev_ptr = xm;
372 	ptr = xm->sibling;
373     }
374 
375     str->buf++;
376     str->len--;
377 
378     if (str->len == 0) {
379 	/* we're there */
380 	if (prev_ptr == NULL)
381 	    *inptr = ptr->sibling;
382 	else
383 	    prev_ptr->sibling = ptr->sibling;
384 	ptr->sibling = NULL;
385 	PutFreeNode(ptr);
386 	return (1);
387     }
388     else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
389 	if (ptr->next != NULL)
390 	    return (0);
391 	if (prev_ptr == NULL)
392 	    *inptr = ptr->sibling;
393 	else
394 	    prev_ptr->sibling = ptr->sibling;
395 	ptr->sibling = NULL;
396 	PutFreeNode(ptr);
397 	return (1);
398     }
399     else {
400 	return (0);
401     }
402 }
403 
404 /* PutFreeNode():
405  *	Puts a tree of nodes onto free list using free(3).
406  */
407 static void
408 PutFreeNode(ptr)
409     XmapNode *ptr;
410 {
411     if (ptr == NULL)
412 	return;
413 
414     if (ptr->next != NULL) {
415 	PutFreeNode(ptr->next);
416 	ptr->next = NULL;
417     }
418 
419     PutFreeNode(ptr->sibling);
420 
421     switch (ptr->type) {
422     case XK_CMD:
423     case XK_NOD:
424 	break;
425     case XK_EXE:
426     case XK_STR:
427 	if (ptr->val.str.buf != NULL)
428 	    xfree((ptr_t) ptr->val.str.buf);
429 	break;
430     default:
431 	abort();
432 	break;
433     }
434     xfree((ptr_t) ptr);
435 }
436 
437 
438 /* GetFreeNode():
439  *	Returns pointer to an XmapNode for ch.
440  */
441 static XmapNode *
442 GetFreeNode(ch)
443     CStr *ch;
444 {
445     XmapNode *ptr;
446 
447     ptr = (XmapNode *) xmalloc((size_t) sizeof(XmapNode));
448     ptr->ch = ch->buf[0];
449     ptr->type = XK_NOD;
450     ptr->val.str.buf = NULL;
451     ptr->val.str.len = 0;
452     ptr->next = NULL;
453     ptr->sibling = NULL;
454     return (ptr);
455 }
456 
457 
458 /* PrintXKey():
459  *	Print the binding associated with Xkey key.
460  *	Print entire Xmap if null
461  */
462 void
463 PrintXkey(key)
464     CStr   *key;
465 {
466     CStr cs;
467 
468     if (key) {
469 	cs.buf = key->buf;
470 	cs.len = key->len;
471     }
472     else {
473 	cs.buf = STRNULL;
474 	cs.len = 0;
475     }
476     /* do nothing if Xmap is empty and null key specified */
477     if (Xmap == NULL && cs.len == 0)
478 	return;
479 
480     printbuf[0] =  '"';
481     if (Lookup(&cs, Xmap, 1) <= -1)
482 	/* key is not bound */
483 	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
484     return;
485 }
486 
487 /* Lookup():
488  *	look for the string starting at node ptr.
489  *	Print if last node
490  */
491 static int
492 Lookup(str, ptr, cnt)
493     CStr   *str;
494     XmapNode *ptr;
495     int     cnt;
496 {
497     int     ncnt;
498 
499     if (ptr == NULL)
500 	return (-1);		/* cannot have null ptr */
501 
502     if (str->len == 0) {
503 	/* no more chars in string.  Enumerate from here. */
504 	(void) Enumerate(ptr, cnt);
505 	return (0);
506     }
507     else {
508 	/* If match put this char into printbuf.  Recurse */
509 	if (ptr->ch == *(str->buf)) {
510 	    /* match found */
511 	    ncnt = unparsech(cnt, &ptr->ch);
512 	    if (ptr->next != NULL) {
513 		/* not yet at leaf */
514 		CStr tstr;
515 		tstr.buf = str->buf + 1;
516 		tstr.len = str->len - 1;
517 		return (Lookup(&tstr, ptr->next, ncnt + 1));
518 	    }
519 	    else {
520 		/* next node is null so key should be complete */
521 		if (str->len == 1) {
522 		    CStr pb;
523 		    printbuf[ncnt + 1] = '"';
524 		    printbuf[ncnt + 2] = '\0';
525 		    pb.buf = printbuf;
526 		    pb.len = ncnt + 2;
527 		    (void) printOne(&pb, &ptr->val, ptr->type);
528 		    return (0);
529 		}
530 		else
531 		    return (-1);/* mismatch -- string still has chars */
532 	    }
533 	}
534 	else {
535 	    /* no match found try sibling */
536 	    if (ptr->sibling)
537 		return (Lookup(str, ptr->sibling, cnt));
538 	    else
539 		return (-1);
540 	}
541     }
542 }
543 
544 static int
545 Enumerate(ptr, cnt)
546     XmapNode *ptr;
547     int     cnt;
548 {
549     int     ncnt;
550 
551     if (cnt >= MAXXKEY - 5) {	/* buffer too small */
552 	printbuf[++cnt] = '"';
553 	printbuf[++cnt] = '\0';
554 	xprintf(CGETS(9, 5,
555 		"Some extended keys too long for internal print buffer"));
556 	xprintf(" \"%S...\"\n", printbuf);
557 	return (0);
558     }
559 
560     if (ptr == NULL) {
561 #ifdef DEBUG_EDIT
562 	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
563 #endif
564 	return (-1);
565     }
566 
567     ncnt = unparsech(cnt, &ptr->ch); /* put this char at end of string */
568     if (ptr->next == NULL) {
569 	CStr pb;
570 	/* print this Xkey and function */
571 	printbuf[++ncnt] = '"';
572 	printbuf[++ncnt] = '\0';
573 	pb.buf = printbuf;
574 	pb.len = ncnt;
575 	(void) printOne(&pb, &ptr->val, ptr->type);
576     }
577     else
578 	(void) Enumerate(ptr->next, ncnt + 1);
579 
580     /* go to sibling if there is one */
581     if (ptr->sibling)
582 	(void) Enumerate(ptr->sibling, cnt);
583     return (0);
584 }
585 
586 
587 /* PrintOne():
588  *	Print the specified key and its associated
589  *	function specified by val
590  */
591 int
592 printOne(key, val, ntype)
593     CStr    *key;
594     XmapVal *val;
595     int      ntype;
596 {
597     struct KeyFuncs *fp;
598     unsigned char unparsbuf[200];
599     static char *fmt = "%s\n";
600 
601     xprintf("%-15S-> ", key->buf);
602     if (val != NULL)
603 	switch (ntype) {
604 	case XK_STR:
605 	case XK_EXE:
606 	    xprintf(fmt, unparsestring(&val->str, unparsbuf,
607 				       ntype == XK_STR ? STRQQ : STRBB));
608 	    break;
609 	case XK_CMD:
610 	    for (fp = FuncNames; fp->name; fp++)
611 		if (val->cmd == fp->func)
612 		    xprintf(fmt, fp->name);
613 		break;
614 	default:
615 	    abort();
616 	    break;
617 	}
618     else
619 	xprintf(fmt, key, CGETS(9, 7, "no input"));
620     return (0);
621 }
622 
623 static int
624 unparsech(cnt, ch)
625     int   cnt;
626     Char  *ch;
627 {
628     if (ch == 0) {
629 	printbuf[cnt++] = '^';
630 	printbuf[cnt] = '@';
631 	return cnt;
632     }
633 
634     if (Iscntrl(*ch)) {
635 #ifdef IS_ASCII
636 	printbuf[cnt++] = '^';
637 	if (*ch == CTL_ESC('\177'))
638 	    printbuf[cnt] = '?';
639 	else
640 	    printbuf[cnt] = *ch | 0100;
641 #else
642 	if (*ch == CTL_ESC('\177'))
643 	{
644 		printbuf[cnt++] = '^';
645 		printbuf[cnt] = '?';
646 	}
647 	else if (Isupper(_toebcdic[_toascii[*ch]|0100])
648 		|| strchr("@[\\]^_", _toebcdic[_toascii[*ch]|0100]) != NULL)
649 	{
650 		printbuf[cnt++] = '^';
651 		printbuf[cnt] = _toebcdic[_toascii[*ch]|0100];
652 	}
653 	else
654 	{
655 		printbuf[cnt++] = '\\';
656 		printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
657 		printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
658 		printbuf[cnt] = (*ch & 7) + '0';
659 	}
660 #endif
661     }
662     else if (*ch == '^') {
663 	printbuf[cnt++] = '\\';
664 	printbuf[cnt] = '^';
665     }
666     else if (*ch == '\\') {
667 	printbuf[cnt++] = '\\';
668 	printbuf[cnt] = '\\';
669     }
670     else if (*ch == ' ' || (Isprint(*ch) && !Isspace(*ch))) {
671 	printbuf[cnt] = *ch;
672     }
673     else {
674 	printbuf[cnt++] = '\\';
675 	printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
676 	printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
677 	printbuf[cnt] = (*ch & 7) + '0';
678     }
679     return cnt;
680 }
681 
682 int
683 parseescape(ptr)
684     const Char  **ptr;
685 {
686     const Char *p;
687     Char c;
688 
689     p = *ptr;
690 
691     if ((p[1] & CHAR) == 0) {
692 	xprintf(CGETS(9, 8, "Something must follow: %c\n"), *p);
693 	return -1;
694     }
695     if ((*p & CHAR) == '\\') {
696 	p++;
697 	switch (*p & CHAR) {
698 	case 'a':
699 	    c = CTL_ESC('\007');         /* Bell */
700 	    break;
701 	case 'b':
702 	    c = CTL_ESC('\010');         /* Backspace */
703 	    break;
704 	case 'e':
705 	    c = CTL_ESC('\033');         /* Escape */
706 	    break;
707 	case 'f':
708 	    c = CTL_ESC('\014');         /* Form Feed */
709 	    break;
710 	case 'n':
711 	    c = CTL_ESC('\012');         /* New Line */
712 	    break;
713 	case 'r':
714 	    c = CTL_ESC('\015');         /* Carriage Return */
715 	    break;
716 	case 't':
717 	    c = CTL_ESC('\011');         /* Horizontal Tab */
718 	    break;
719 	case 'v':
720 	    c = CTL_ESC('\013');         /* Vertical Tab */
721 	    break;
722 	case '0':
723 	case '1':
724 	case '2':
725 	case '3':
726 	case '4':
727 	case '5':
728 	case '6':
729 	case '7':
730 	    {
731 		register int cnt, val, 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++ = (unsigned char) p;
828 	}
829 	else {
830 	    *b++ = '\\';
831 	    *b++ = ((p >> 6) & 7) + '0';
832 	    *b++ = ((p >> 3) & 7) + '0';
833 	    *b++ = (p & 7) + '0';
834 	}
835     }
836     if (sep[0] && sep[1])
837 #ifndef WINNT_NATIVE
838 	*b++ = sep[1];
839 #else /* WINNT_NATIVE */
840 	*b++ = CHAR & sep[1];
841 #endif /* !WINNT_NATIVE */
842     *b++ = 0;
843     return buf;			/* should check for overflow */
844 }
845