ed.xmap.c revision 100616
1/* $Header: /src/pub/tcsh/ed.xmap.c,v 3.25 2002/03/08 17:36:45 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
91RCSID("$Id: ed.xmap.c,v 3.25 2002/03/08 17:36:45 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 */
105typedef 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
114static XmapNode *Xmap = NULL;	/* the current Xmap */
115#define MAXXKEY 100		/* max length of a Xkey for print putposes */
116static Char printbuf[MAXXKEY];	/* buffer for printing */
117
118
119/* Some declarations of procedures */
120static	int       TraverseMap	__P((XmapNode *, CStr *, XmapVal *));
121static	int       TryNode	__P((XmapNode *, CStr *, XmapVal *, int));
122static	XmapNode *GetFreeNode	__P((CStr *));
123static	void	  PutFreeNode	__P((XmapNode *));
124static	int	  TryDeleteNode	__P((XmapNode **, CStr *));
125static	int	  Lookup	__P((CStr *, XmapNode *, int));
126static	int	  Enumerate	__P((XmapNode *, int));
127static	int	  unparsech	__P((int, Char *));
128
129
130XmapVal *
131XmapCmd(cmd)
132    int cmd;
133{
134    static XmapVal xm;
135    xm.cmd = (KEYCMD) cmd;
136    return &xm;
137}
138
139XmapVal *
140XmapStr(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 */
153void
154ResetXmap()
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 */
167int
168GetXkey(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 */
179static int
180TraverseMap(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
220void
221AddXkey(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
248static int
249TryNode(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
319void
320ClearXkey(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
331int
332DeleteXkey(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
347static int
348TryDeleteNode(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 */
403static void
404PutFreeNode(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 */
437static XmapNode *
438GetFreeNode(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 */
458void
459PrintXkey(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 */
487static int
488Lookup(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
540static int
541Enumerate(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 */
587int
588printOne(key, val, ntype)
589    CStr    *key;
590    XmapVal *val;
591    int      ntype;
592{
593    struct KeyFuncs *fp;
594    unsigned char unparsbuf[200];
595    static 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
619static int
620unparsech(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
678int
679parseescape(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 -1;
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		register int cnt, val, ch;
731
732		for (cnt = 0, val = 0; cnt < 3; cnt++) {
733		    ch = *p++ & CHAR;
734		    if (ch < '0' || ch > '7') {
735			p--;
736			break;
737		    }
738		    val = (val << 3) | (ch - '0');
739		}
740		if ((val & 0xffffff00) != 0) {
741		    xprintf(CGETS(9, 9,
742			    "Octal constant does not fit in a char.\n"));
743		    return 0;
744		}
745#ifndef IS_ASCII
746		if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
747		    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
748			    "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/);
749#endif
750		c = (Char) val;
751		--p;
752	    }
753	    break;
754	default:
755	    c = *p;
756	    break;
757	}
758    }
759    else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
760				    strchr("@^_?\\|[{]}", p[1] & CHAR))) {
761	p++;
762#ifdef IS_ASCII
763	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
764#else
765	c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
766	if (adrof(STRwarnebcdic))
767	    xprintf(/*CGETS(9, 9, no NLS-String yet!*/
768		"Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/);
769#endif
770    }
771    else
772	c = *p;
773    *ptr = p;
774    return (c);
775}
776
777
778unsigned char *
779unparsestring(str, buf, sep)
780    CStr   *str;
781    unsigned char *buf;
782    Char   *sep;
783{
784    unsigned char *b;
785    Char   p;
786    int l;
787
788    b = buf;
789    if (sep[0])
790#ifndef WINNT_NATIVE
791	*b++ = sep[0];
792#else /* WINNT_NATIVE */
793	*b++ = CHAR & sep[0];
794#endif /* !WINNT_NATIVE */
795
796    for (l = 0; l < str->len; l++) {
797	p = str->buf[l];
798	if (Iscntrl(p)) {
799#ifdef IS_ASCII
800	    *b++ = '^';
801	    if (p == CTL_ESC('\177'))
802		*b++ = '?';
803	    else
804		*b++ = (unsigned char) (p | 0100);
805#else
806	    if (_toascii[p] == '\177' || Isupper(_toebcdic[_toascii[p]|0100])
807		 || strchr("@[\\]^_", _toebcdic[_toascii[p]|0100]) != NULL)
808	    {
809		*b++ = '^';
810		*b++ = (_toascii[p] == '\177') ? '?' : _toebcdic[_toascii[p]|0100];
811	    }
812	    else
813	    {
814		*b++ = '\\';
815		*b++ = ((p >> 6) & 7) + '0';
816		*b++ = ((p >> 3) & 7) + '0';
817		*b++ = (p & 7) + '0';
818	    }
819#endif
820	}
821	else if (p == '^' || p == '\\') {
822	    *b++ = '\\';
823	    *b++ = (unsigned char) p;
824	}
825	else if (p == ' ' || (Isprint(p) && !Isspace(p))) {
826	    *b++ = (unsigned char) p;
827	}
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