ed.xmap.c revision 59243
1/* $Header: /src/pub/tcsh/ed.xmap.c,v 3.21 1999/06/01 20:01:32 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
95RCSID("$Id: ed.xmap.c,v 3.21 1999/06/01 20:01:32 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 */
109typedef 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
118static XmapNode *Xmap = NULL;	/* the current Xmap */
119#define MAXXKEY 100		/* max length of a Xkey for print putposes */
120static Char printbuf[MAXXKEY];	/* buffer for printing */
121
122
123/* Some declarations of procedures */
124static	int       TraverseMap	__P((XmapNode *, CStr *, XmapVal *));
125static	int       TryNode	__P((XmapNode *, CStr *, XmapVal *, int));
126static	XmapNode *GetFreeNode	__P((CStr *));
127static	void	  PutFreeNode	__P((XmapNode *));
128static	int	  TryDeleteNode	__P((XmapNode **, CStr *));
129static	int	  Lookup	__P((CStr *, XmapNode *, int));
130static	int	  Enumerate	__P((XmapNode *, int));
131static	int	  unparsech	__P((int, Char *));
132
133
134XmapVal *
135XmapCmd(cmd)
136    int cmd;
137{
138    static XmapVal xm;
139    xm.cmd = (KEYCMD) cmd;
140    return &xm;
141}
142
143XmapVal *
144XmapStr(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 */
157void
158ResetXmap()
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 */
171int
172GetXkey(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 */
183static int
184TraverseMap(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
224void
225AddXkey(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
252static int
253TryNode(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
323void
324ClearXkey(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
335int
336DeleteXkey(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
351static int
352TryDeleteNode(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 */
407static void
408PutFreeNode(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 */
441static XmapNode *
442GetFreeNode(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 */
462void
463PrintXkey(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 */
491static int
492Lookup(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
544static int
545Enumerate(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 */
591int
592printOne(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
623static int
624unparsech(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#ifndef _OSD_POSIX
636	printbuf[cnt++] = '^';
637	if (*ch == CTL_ESC('\177'))
638	    printbuf[cnt] = '?';
639	else
640	    printbuf[cnt] = *ch | 0100;
641#else /*_OSD_POSIX*/
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 /*_OSD_POSIX*/
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
682int
683parseescape(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#ifdef _OSD_POSIX
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#ifndef _OSD_POSIX
764	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
765#else /*_OSD_POSIX*/
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 /*_OSD_POSIX*/
771    }
772    else
773	c = *p;
774    *ptr = p;
775    return (c);
776}
777
778
779unsigned char *
780unparsestring(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
792	*b++ = sep[0];
793#else /* WINNT */
794	*b++ = CHAR & sep[0];
795#endif /* !WINNT */
796
797    for (l = 0; l < str->len; l++) {
798	p = str->buf[l];
799	if (Iscntrl(p)) {
800#ifndef _OSD_POSIX
801	    *b++ = '^';
802	    if (p == CTL_ESC('\177'))
803		*b++ = '?';
804	    else
805		*b++ = (unsigned char) (p | 0100);
806#else /*_OSD_POSIX*/
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 /*_OSD_POSIX*/
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
838	*b++ = sep[1];
839#else /* WINNT */
840	*b++ = CHAR & sep[1];
841#endif /* !WINNT */
842    *b++ = 0;
843    return buf;			/* should check for overflow */
844}
845