Merge from vendor branch LIBSTDC++:
[dragonfly.git] / contrib / tcsh / ed.xmap.c
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
91 RCSID("$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  */
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 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 int
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 -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
778 unsigned char *
779 unparsestring(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 }