xref: /dragonfly/contrib/tcsh-6/ed.xmap.c (revision 84d884bf08edef6c02f15218458cd5df8010b654)
1 /*
2  * ed.xmap.c: This module contains the procedures for maintaining
3  *              the extended-key map.
4  *
5  *              An extended-key (Xkey) is a sequence of keystrokes
6  *              introduced with an sequence introducer and consisting
7  *              of an arbitrary number of characters.  This module maintains
8  *              a map (the Xmap) to convert these extended-key sequences
9  *              into input strings (XK_STR), editor functions (XK_CMD), or
10  *              unix commands (XK_EXE). It contains the
11  *              following externally visible functions.
12  *
13  *                  int GetXkey(ch,val);
14  *                  CStr *ch;
15  *                  XmapVal *val;
16  *
17  *              Looks up *ch in map and then reads characters until a
18  *              complete match is found or a mismatch occurs. Returns the
19  *              type of the match found (XK_STR, XK_CMD, or XK_EXE).
20  *              Returns NULL in val.str and XK_STR for no match.
21  *              The last character read is returned in *ch.
22  *
23  *                  void AddXkey(Xkey, val, ntype);
24  *                  CStr *Xkey;
25  *                  XmapVal *val;
26  *                  int ntype;
27  *
28  *              Adds Xkey to the Xmap and associates the value in val with it.
29  *              If Xkey is already is in Xmap, the new code is applied to the
30  *              existing Xkey. Ntype specifies if code is a command, an
31  *              out string or a unix command.
32  *
33  *                int DeleteXkey(Xkey);
34  *                CStr *Xkey;
35  *
36  *              Delete the Xkey and all longer Xkeys staring with Xkey, if
37  *              they exists.
38  *
39  *              Warning:
40  *                  If Xkey is a substring of some other Xkeys, then the longer
41  *                  Xkeys are lost!!  That is, if the Xkeys "abcd" and "abcef"
42  *                  are in Xmap, adding the key "abc" will cause the first two
43  *                  definitions to be lost.
44  *
45  *                  void ResetXmap();
46  *
47  *              Removes all entries from Xmap and resets the defaults.
48  *
49  *                  void PrintXkey(Xkey);
50  *                  CStr *Xkey;
51  *
52  *              Prints all extended keys prefixed by Xkey and their associated
53  *              commands.
54  *
55  *              Restrictions:
56  *              -------------
57  *                1) It is not possible to have one Xkey that is a
58  *                     substring of another.
59  */
60 /*-
61  * Copyright (c) 1980, 1991 The Regents of the University of California.
62  * All rights reserved.
63  *
64  * Redistribution and use in source and binary forms, with or without
65  * modification, are permitted provided that the following conditions
66  * are met:
67  * 1. Redistributions of source code must retain the above copyright
68  *    notice, this list of conditions and the following disclaimer.
69  * 2. Redistributions in binary form must reproduce the above copyright
70  *    notice, this list of conditions and the following disclaimer in the
71  *    documentation and/or other materials provided with the distribution.
72  * 3. Neither the name of the University nor the names of its contributors
73  *    may be used to endorse or promote products derived from this software
74  *    without specific prior written permission.
75  *
76  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
77  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
78  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
79  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
80  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
81  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
82  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
83  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
84  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
85  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
86  * SUCH DAMAGE.
87  */
88 #include "sh.h"
89 #include "ed.h"
90 #include "ed.defns.h"
91 
92 #ifndef NULL
93 #define NULL 0
94 #endif
95 
96 /* Internal Data types and declarations */
97 
98 /* The Nodes of the Xmap.  The Xmap is a linked list of these node
99  * elements
100  */
101 typedef struct Xmapnode {
102     Char    ch;                         /* single character of Xkey */
103     int     type;
104     XmapVal val;              /* command code or pointer to string, if this
105                                          * is a leaf */
106     struct Xmapnode *next;    /* ptr to next char of this Xkey */
107     struct Xmapnode *sibling; /* ptr to another Xkey with same prefix */
108 } XmapNode;
109 
110 static XmapNode *Xmap = NULL; /* the current Xmap */
111 
112 
113 /* Some declarations of procedures */
114 static    int       TraverseMap         (XmapNode *, CStr *, XmapVal *);
115 static    int       TryNode   (XmapNode *, CStr *, XmapVal *, int);
116 static    XmapNode *GetFreeNode         (CStr *);
117 static    void        PutFreeNode       (XmapNode *);
118 static    int         TryDeleteNode     (XmapNode **, CStr *);
119 static    int         Lookup  (struct Strbuf *, const CStr *,
120                                          const XmapNode *);
121 static    void        Enumerate         (struct Strbuf *, const XmapNode *);
122 static    void        unparsech         (struct Strbuf *, Char);
123 
124 
125 XmapVal *
XmapCmd(int cmd)126 XmapCmd(int cmd)
127 {
128     static XmapVal xm;
129     xm.cmd = (KEYCMD) cmd;
130     return &xm;
131 }
132 
133 XmapVal *
XmapStr(CStr * str)134 XmapStr(CStr *str)
135 {
136     static XmapVal xm;
137     xm.str.len = str->len;
138     xm.str.buf = str->buf;
139     return &xm;
140 }
141 
142 /* ResetXmap():
143  *        Takes all nodes on Xmap and puts them on free list.  Then
144  *        initializes Xmap with arrow keys
145  */
146 void
ResetXmap(void)147 ResetXmap(void)
148 {
149     PutFreeNode(Xmap);
150     Xmap = NULL;
151 
152     DefaultArrowKeys();
153     return;
154 }
155 
156 
157 /* GetXkey():
158  *        Calls the recursive function with entry point Xmap
159  */
160 int
GetXkey(CStr * ch,XmapVal * val)161 GetXkey(CStr *ch, XmapVal *val)
162 {
163     return (TraverseMap(Xmap, ch, val));
164 }
165 
166 /* TraverseMap():
167  *        recursively traverses node in tree until match or mismatch is
168  *        found.  May read in more characters.
169  */
170 static int
TraverseMap(XmapNode * ptr,CStr * ch,XmapVal * val)171 TraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val)
172 {
173     Char    tch;
174 
175     if (ptr->ch == *(ch->buf)) {
176           /* match found */
177           if (ptr->next) {
178               /* Xkey not complete so get next char */
179               if (GetNextChar(&tch) != 1) {       /* if EOF or error */
180                     val->cmd = F_SEND_EOF;
181                     return XK_CMD;/* PWP: Pretend we just read an end-of-file */
182               }
183               *(ch->buf) = tch;
184               return (TraverseMap(ptr->next, ch, val));
185           }
186           else {
187               *val = ptr->val;
188               if (ptr->type != XK_CMD)
189                     *(ch->buf) = '\0';
190               return ptr->type;
191           }
192     }
193     else {
194           /* no match found here */
195           if (ptr->sibling) {
196               /* try next sibling */
197               return (TraverseMap(ptr->sibling, ch, val));
198           }
199           else {
200               /* no next sibling -- mismatch */
201               val->str.buf = NULL;
202               val->str.len = 0;
203               return XK_STR;
204           }
205     }
206 }
207 
208 void
AddXkey(const CStr * Xkey,XmapVal * val,int ntype)209 AddXkey(const CStr *Xkey, XmapVal *val, int ntype)
210 {
211     CStr cs;
212     cs.buf = Xkey->buf;
213     cs.len = Xkey->len;
214     if (Xkey->len == 0) {
215           xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n"));
216           return;
217     }
218 
219     if (ntype == XK_CMD && val->cmd == F_XKEY) {
220           xprintf("%s",
221               CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
222           return;
223     }
224 
225     if (Xmap == NULL)
226           /* tree is initially empty.  Set up new node to match Xkey[0] */
227           Xmap = GetFreeNode(&cs);      /* it is properly initialized */
228 
229     /* Now recurse through Xmap */
230     (void) TryNode(Xmap, &cs, val, ntype);
231     return;
232 }
233 
234 static int
TryNode(XmapNode * ptr,CStr * str,XmapVal * val,int ntype)235 TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype)
236 {
237     /*
238      * Find a node that matches *string or allocate a new one
239      */
240     if (ptr->ch != *(str->buf)) {
241           XmapNode *xm;
242 
243           for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
244               if (xm->sibling->ch == *(str->buf))
245                     break;
246           if (xm->sibling == NULL)
247               xm->sibling = GetFreeNode(str);     /* setup new node */
248           ptr = xm->sibling;
249     }
250 
251     str->buf++;
252     str->len--;
253     if (str->len == 0) {
254           size_t len;
255 
256           /* we're there */
257           if (ptr->next != NULL) {
258               PutFreeNode(ptr->next);   /* lose longer Xkeys with this prefix */
259               ptr->next = NULL;
260           }
261 
262           switch (ptr->type) {
263           case XK_STR:
264           case XK_EXE:
265               xfree(ptr->val.str.buf);
266               ptr->val.str.len = 0;
267               break;
268           case XK_NOD:
269           case XK_CMD:
270               break;
271           default:
272               abort();
273               break;
274           }
275 
276           switch (ptr->type = ntype) {
277           case XK_CMD:
278               ptr->val = *val;
279               break;
280           case XK_STR:
281           case XK_EXE:
282               ptr->val.str.len = val->str.len;
283               len = (val->str.len + 1) * sizeof(*ptr->val.str.buf);
284               ptr->val.str.buf = xmalloc(len);
285               (void) memcpy(ptr->val.str.buf, val->str.buf, len);
286               break;
287           default:
288               abort();
289               break;
290           }
291     }
292     else {
293           /* still more chars to go */
294           if (ptr->next == NULL)
295               ptr->next = GetFreeNode(str);       /* setup new node */
296           (void) TryNode(ptr->next, str, val, ntype);
297     }
298     return (0);
299 }
300 
301 void
ClearXkey(KEYCMD * map,const CStr * in)302 ClearXkey(KEYCMD *map, const CStr *in)
303 {
304     unsigned char c = (unsigned char) *(in->buf);
305     if ((map[c] == F_XKEY) &&
306           ((map == CcKeyMap && CcAltMap[c] != F_XKEY) ||
307            (map == CcAltMap && CcKeyMap[c] != F_XKEY)))
308           (void) DeleteXkey(in);
309 }
310 
311 int
DeleteXkey(const CStr * Xkey)312 DeleteXkey(const CStr *Xkey)
313 {
314     CStr s;
315 
316     s = *Xkey;
317     if (s.len == 0) {
318           xprintf("%s",
319                   CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
320           return (-1);
321     }
322 
323     if (Xmap == NULL)
324           return (0);
325 
326     (void) TryDeleteNode(&Xmap, &s);
327     return (0);
328 }
329 
330 /* Destroys str */
331 static int
TryDeleteNode(XmapNode ** inptr,CStr * str)332 TryDeleteNode(XmapNode **inptr, CStr *str)
333 {
334     XmapNode *ptr;
335 
336     ptr = *inptr;
337     /*
338      * Find a node that matches *string or allocate a new one
339      */
340     if (ptr->ch != *(str->buf)) {
341           XmapNode *xm;
342 
343           for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
344               if (xm->sibling->ch == *(str->buf))
345                     break;
346           if (xm->sibling == NULL)
347               return (0);
348           inptr = &xm->sibling;
349           ptr = xm->sibling;
350     }
351 
352     str->buf++;
353     str->len--;
354 
355     if (str->len == 0) {
356           /* we're there */
357           *inptr = ptr->sibling;
358           ptr->sibling = NULL;
359           PutFreeNode(ptr);
360           return (1);
361     }
362     else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) {
363           if (ptr->next != NULL)
364               return (0);
365           *inptr = ptr->sibling;
366           ptr->sibling = NULL;
367           PutFreeNode(ptr);
368           return (1);
369     }
370     else {
371           return (0);
372     }
373 }
374 
375 /* PutFreeNode():
376  *        Puts a tree of nodes onto free list using free(3).
377  */
378 static void
PutFreeNode(XmapNode * ptr)379 PutFreeNode(XmapNode *ptr)
380 {
381     if (ptr == NULL)
382           return;
383 
384     if (ptr->next != NULL) {
385           PutFreeNode(ptr->next);
386           ptr->next = NULL;
387     }
388 
389     PutFreeNode(ptr->sibling);
390 
391     switch (ptr->type) {
392     case XK_CMD:
393     case XK_NOD:
394           break;
395     case XK_EXE:
396     case XK_STR:
397           xfree(ptr->val.str.buf);
398           break;
399     default:
400           abort();
401           break;
402     }
403     xfree(ptr);
404 }
405 
406 
407 /* GetFreeNode():
408  *        Returns pointer to an XmapNode for ch.
409  */
410 static XmapNode *
GetFreeNode(CStr * ch)411 GetFreeNode(CStr *ch)
412 {
413     XmapNode *ptr;
414 
415     ptr = xmalloc(sizeof(XmapNode));
416     ptr->ch = ch->buf[0];
417     ptr->type = XK_NOD;
418     ptr->val.str.buf = NULL;
419     ptr->val.str.len = 0;
420     ptr->next = NULL;
421     ptr->sibling = NULL;
422     return (ptr);
423 }
424 
425 
426 /* PrintXKey():
427  *        Print the binding associated with Xkey key.
428  *        Print entire Xmap if null
429  */
430 void
PrintXkey(const CStr * key)431 PrintXkey(const CStr *key)
432 {
433     struct Strbuf buf = Strbuf_INIT;
434     CStr cs;
435 
436     if (key) {
437           cs.buf = key->buf;
438           cs.len = key->len;
439     }
440     else {
441           cs.buf = STRNULL;
442           cs.len = 0;
443     }
444     /* do nothing if Xmap is empty and null key specified */
445     if (Xmap == NULL && cs.len == 0)
446           return;
447 
448     Strbuf_append1(&buf, '"');
449     cleanup_push(&buf, Strbuf_cleanup);
450     if (Lookup(&buf, &cs, Xmap) <= -1)
451           /* key is not bound */
452           xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
453     cleanup_until(&buf);
454 }
455 
456 /* Lookup():
457  *        look for the string starting at node ptr.
458  *        Print if last node
459  */
460 static int
Lookup(struct Strbuf * buf,const CStr * str,const XmapNode * ptr)461 Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
462 {
463     if (ptr == NULL)
464           return (-1);                  /* cannot have null ptr */
465 
466     if (str->len == 0) {
467           /* no more chars in string.  Enumerate from here. */
468           Enumerate(buf, ptr);
469           return (0);
470     }
471     else {
472           /* If match put this char into buf.  Recurse */
473           if (ptr->ch == *(str->buf)) {
474               /* match found */
475               unparsech(buf, ptr->ch);
476               if (ptr->next != NULL) {
477                     /* not yet at leaf */
478                     CStr tstr;
479                     tstr.buf = str->buf + 1;
480                     tstr.len = str->len - 1;
481                     return (Lookup(buf, &tstr, ptr->next));
482               }
483               else {
484                     /* next node is null so key should be complete */
485                     if (str->len == 1) {
486                         Strbuf_append1(buf, '"');
487                         Strbuf_terminate(buf);
488                         printOne(buf->s, &ptr->val, ptr->type);
489                         return (0);
490                     }
491                     else
492                         return (-1);/* mismatch -- string still has chars */
493               }
494           }
495           else {
496               /* no match found try sibling */
497               if (ptr->sibling)
498                     return (Lookup(buf, str, ptr->sibling));
499               else
500                     return (-1);
501           }
502     }
503 }
504 
505 static void
Enumerate(struct Strbuf * buf,const XmapNode * ptr)506 Enumerate(struct Strbuf *buf, const XmapNode *ptr)
507 {
508     size_t old_len;
509 
510     if (ptr == NULL) {
511 #ifdef DEBUG_EDIT
512           xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
513 #endif
514           return;
515     }
516 
517     old_len = buf->len;
518     unparsech(buf, ptr->ch); /* put this char at end of string */
519     if (ptr->next == NULL) {
520           /* print this Xkey and function */
521           Strbuf_append1(buf, '"');
522           Strbuf_terminate(buf);
523           printOne(buf->s, &ptr->val, ptr->type);
524     }
525     else
526           Enumerate(buf, ptr->next);
527 
528     /* go to sibling if there is one */
529     if (ptr->sibling) {
530           buf->len = old_len;
531           Enumerate(buf, ptr->sibling);
532     }
533 }
534 
535 
536 /* PrintOne():
537  *        Print the specified key and its associated
538  *        function specified by val
539  */
540 void
printOne(const Char * key,const XmapVal * val,int ntype)541 printOne(const Char *key, const XmapVal *val, int ntype)
542 {
543     struct KeyFuncs *fp;
544     static const char *fmt = "%s\n";
545 
546     xprintf("%-15" TCSH_S "-> ", key);
547     if (val != NULL)
548           switch (ntype) {
549           case XK_STR:
550           case XK_EXE: {
551               unsigned char *p;
552 
553               p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB);
554               cleanup_push(p, xfree);
555               xprintf(fmt, p);
556               cleanup_until(p);
557               break;
558           }
559           case XK_CMD:
560               for (fp = FuncNames; fp->name; fp++)
561                     if (val->cmd == fp->func)
562                         xprintf(fmt, fp->name);
563               break;
564           default:
565               abort();
566               break;
567           }
568     else
569           xprintf(fmt, CGETS(9, 7, "no input"));
570 }
571 
572 static void
unparsech(struct Strbuf * buf,Char ch)573 unparsech(struct Strbuf *buf, Char ch)
574 {
575     if (ch == 0) {
576           Strbuf_append1(buf, '^');
577           Strbuf_append1(buf, '@');
578     }
579     else if (Iscntrl(ch)) {
580           Strbuf_append1(buf, '^');
581           if (ch == CTL_ESC('\177'))
582               Strbuf_append1(buf, '?');
583           else
584 #ifdef IS_ASCII
585               Strbuf_append1(buf, ch | 0100);
586 #else
587               Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
588 #endif
589     }
590     else if (ch == '^') {
591           Strbuf_append1(buf, '\\');
592           Strbuf_append1(buf, '^');
593     } else if (ch == '\\') {
594           Strbuf_append1(buf, '\\');
595           Strbuf_append1(buf, '\\');
596     } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
597           Strbuf_append1(buf, ch);
598     }
599     else {
600           Strbuf_append1(buf, '\\');
601           Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
602           Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
603           Strbuf_append1(buf, (ch & 7) + '0');
604     }
605 }
606 
607 static Char
parse_hex_range(const Char ** cp,size_t l)608 parse_hex_range(const Char **cp, size_t l)
609 {
610     size_t ui = 0;
611     Char r = 0, c;
612 
613     if (l > 8)
614           abort();
615 
616     for (; (c = (**cp & CHAR)) != '\0' && ui < l && Isxdigit(c); (*cp)++, ui++) {
617           Char x = Isdigit(c) ? '0' : ((Isupper(c) ? 'A' : 'a') - 10);
618 #ifndef IS_ASCII
619           c = _toascii(c);
620 #endif
621           r <<= 4;
622           r |= c - x;
623     }
624     (*cp)--;
625 #ifndef IS_ASCII
626     return _toebcdic[r & 0xff];
627 #else
628     return r;
629 #endif
630 }
631 
632 /*
633  * The e parameter is false when we are doing echo and true when we are
634  * using it to parse other escaped strings. For echo, we don't print errors
635  * and we don't unescape backslashes that we don't understand. Perhaps we
636  * always not unescape backslashes we don't understand...
637  */
638 eChar
parseescape(const Char ** ptr,int e)639 parseescape(const Char **ptr, int e)
640 {
641     const Char *p;
642     Char c;
643 
644     p = *ptr;
645 
646     if ((p[1] & CHAR) == 0) {
647           if (e)
648               xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p);
649           return CHAR_ERR;
650     }
651     if ((*p & CHAR) == '\\') {
652           p++;
653           switch (*p & CHAR) {
654           case 'a':
655               c = CTL_ESC('\007');         /* Bell */
656               break;
657           case 'b':
658               c = CTL_ESC('\010');         /* Backspace */
659               break;
660           case 'c':
661               p++;
662               if ((*p & CHAR) == '\\') {
663                     p++;
664                     if ((*p & CHAR) != '\\') {
665                         *ptr = p;
666                         if (e)
667                               xprintf(/*CGETS(9, 9, */
668                                   "Invalid escape sequence: %s%c\n"/*)*/, "\\c\\", (char)*p);
669                         return CHAR_ERR;
670                     }
671                     c = (*p & CHAR) & 0237;
672               } else if ((Isalpha(*p & CHAR) || strchr("@^_?\\|[{]}", *p & CHAR))) {
673                     /* XXX: Duplicate code from ^ below */
674 #ifdef IS_ASCII
675                     c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
676 #else
677                     c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
678                     if (adrof(STRwarnebcdic))
679                         xprintf(/*CGETS(9, 9, no NLS-String yet!*/
680                               "Warning: Control character %s%c may be interpreted differently in EBCDIC.\n", "\\c", *p & CHAR /*)*/);
681 #endif
682               } else { /* backward compat */
683                     c = '\\';
684                     p -= 2;
685               }
686               break;
687           case 'e':
688               c = CTL_ESC('\033');         /* Escape */
689               break;
690           case 'f':
691               c = CTL_ESC('\014');         /* Form Feed */
692               break;
693           case 'n':
694               c = CTL_ESC('\012');         /* New Line */
695               break;
696           case 'r':
697               c = CTL_ESC('\015');         /* Carriage Return */
698               break;
699           case 't':
700               c = CTL_ESC('\011');         /* Horizontal Tab */
701               break;
702           case 'v':
703               c = CTL_ESC('\013');         /* Vertical Tab */
704               break;
705           case '\\':
706               c = '\\';
707               break;
708           case 'x':
709               p++;
710               if ((*p & CHAR) == '{' && Isxdigit(*(p + 1) & CHAR)) { /* \x{20ac} */
711                     const Char *q = p - 2;
712                     p++;
713                     c = parse_hex_range(&p, 8);
714                     if ((p[1] & CHAR) != '}') {
715                         if (e)
716                               xprintf("%s", /* CGETS(9, 9, */
717                                   "Missing closing brace");
718                         p = q;
719                         c = '\\';
720                         break;
721                     }
722                     p++;
723               } else if (Isxdigit(*p & CHAR)) {   /* \x9f */
724                     c = parse_hex_range(&p, 2);
725               } else { /* backward compat */
726                     c = '\\';
727                     p -= 2;
728               }
729               break;
730           case 'u':
731           case 'U':
732               {
733                     size_t limit = (*p & CHAR) == 'u' ? 4 : 8;
734                     p++;
735                     if (Isxdigit(*p & CHAR)) {    /* \u20ac or \U000020ac */
736                         c = parse_hex_range(&p, limit);
737                     } else { /* backward compat */
738                         c = '\\';
739                         p -= 2;
740                     }
741               }
742               break;
743           case '0':
744           case '1':
745           case '2':
746           case '3':
747           case '4':
748           case '5':
749           case '6':
750           case '7':
751               {
752                     int cnt, val;
753                     Char ch;
754 
755                     for (cnt = 0, val = 0; cnt < 3; cnt++) {
756                         ch = *p++ & CHAR;
757                         if (ch < '0' || ch > '7') {
758                               p--;
759                               break;
760                         }
761                         val = (val << 3) | (ch - '0');
762                     }
763                     if ((val & ~0xff) != 0) {
764                         if (e)
765                               xprintf("%s", CGETS(9, 9,
766                                   "Octal constant does not fit in a char.\n"));
767                         *ptr = p;
768                         return CHAR_ERR;
769                     }
770 #ifndef IS_ASCII
771                     if (CTL_ESC(val) != val && adrof(STRwarnebcdic))
772                         xprintf(/*CGETS(9, 9, no NLS-String yet!*/
773                               "Warning: Octal constant \\%3.3o is interpreted as "
774                               "EBCDIC value.\n", val/*)*/);
775 #endif
776                     c = (Char) val;
777                     --p;
778               }
779               break;
780           default:
781               if (!e)
782                     --p;
783               c = *p;
784               break;
785           }
786     }
787     else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) ||
788                                             strchr("@^_?\\|[{]}", p[1] & CHAR))) {
789           p++;
790 #ifdef IS_ASCII
791           c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237);
792 #else
793           c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237];
794           if (adrof(STRwarnebcdic))
795               xprintf(/*CGETS(9, 9, no NLS-String yet!*/
796                     "Warning: Control character %s%c may be interpreted differently in EBCDIC.\n", "^", *p & CHAR /*)*/);
797 #endif
798     }
799     else
800           c = *p & CHAR;
801     *ptr = p;
802     return (c);
803 }
804 
805 
806 unsigned char *
unparsestring(const CStr * str,const Char * sep)807 unparsestring(const CStr *str, const Char *sep)
808 {
809     unsigned char *buf, *b;
810     Char   p;
811     int l;
812 
813     /* Worst-case is "\uuu" or result of wctomb() for each char from str */
814     buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX));
815     b = buf;
816     if (sep[0])
817 #ifndef WINNT_NATIVE
818           *b++ = sep[0];
819 #else /* WINNT_NATIVE */
820           *b++ = CHAR & sep[0];
821 #endif /* !WINNT_NATIVE */
822 
823     for (l = 0; l < str->len; l++) {
824           p = str->buf[l];
825           if (Iscntrl(p)) {
826               *b++ = '^';
827               if (p == CTL_ESC('\177'))
828                     *b++ = '?';
829               else
830 #ifdef IS_ASCII
831                     *b++ = (unsigned char) (p | 0100);
832 #else
833                     *b++ = _toebcdic[_toascii[p]|0100];
834 #endif
835           }
836           else if (p == '^' || p == '\\') {
837               *b++ = '\\';
838               *b++ = (unsigned char) p;
839           }
840           else if (p == ' ' || (Isprint(p) && !Isspace(p)))
841               b += one_wctomb((char *)b, p);
842           else {
843               *b++ = '\\';
844               *b++ = ((p >> 6) & 7) + '0';
845               *b++ = ((p >> 3) & 7) + '0';
846               *b++ = (p & 7) + '0';
847           }
848     }
849     if (sep[0] && sep[1])
850 #ifndef WINNT_NATIVE
851           *b++ = sep[1];
852 #else /* WINNT_NATIVE */
853           *b++ = CHAR & sep[1];
854 #endif /* !WINNT_NATIVE */
855     *b++ = 0;
856     return buf;                         /* should check for overflow */
857 }
858