Kernel part of bluetooth stack ported by Dmitry Komissaroff. Very much work
[dragonfly.git] / contrib / tcsh / ed.xmap.c
blob47365d7d8d4518eaa8c00eef08a6bb6d3f5f93fe
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.
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.
14 * int GetXkey(ch,val);
15 * CStr *ch;
16 * XmapVal *val;
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.
24 * void AddXkey(Xkey, val, ntype);
25 * CStr *Xkey;
26 * XmapVal *val;
27 * int ntype;
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.
34 * int DeleteXkey(Xkey);
35 * CStr *Xkey;
37 * Delete the Xkey and all longer Xkeys staring with Xkey, if
38 * they exists.
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.
46 * void ResetXmap();
48 * Removes all entries from Xmap and resets the defaults.
50 * void PrintXkey(Xkey);
51 * CStr *Xkey;
53 * Prints all extended keys prefixed by Xkey and their associated
54 * commands.
56 * Restrictions:
57 * -------------
58 * 1) It is not possible to have one Xkey that is a
59 * substring of another.
61 /*-
62 * Copyright (c) 1980, 1991 The Regents of the University of California.
63 * All rights reserved.
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.
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.
89 #include "sh.h"
91 RCSID("$Id: ed.xmap.c,v 3.25 2002/03/08 17:36:45 christos Exp $")
93 #include "ed.h"
94 #include "ed.defns.h"
96 #ifndef NULL
97 #define NULL 0
98 #endif
100 /* Internal Data types and declarations */
102 /* The Nodes of the Xmap. The Xmap is a linked list of these node
103 * elements
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;
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 */
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 *));
130 XmapVal *
131 XmapCmd(cmd)
132 int cmd;
134 static XmapVal xm;
135 xm.cmd = (KEYCMD) cmd;
136 return &xm;
139 XmapVal *
140 XmapStr(str)
141 CStr *str;
143 static XmapVal xm;
144 xm.str.len = str->len;
145 xm.str.buf = str->buf;
146 return &xm;
149 /* ResetXmap():
150 * Takes all nodes on Xmap and puts them on free list. Then
151 * initializes Xmap with arrow keys
153 void
154 ResetXmap()
156 PutFreeNode(Xmap);
157 Xmap = NULL;
159 DefaultArrowKeys();
160 return;
164 /* GetXkey():
165 * Calls the recursive function with entry point Xmap
168 GetXkey(ch, val)
169 CStr *ch;
170 XmapVal *val;
172 return (TraverseMap(Xmap, ch, val));
175 /* TraverseMap():
176 * recursively traverses node in tree until match or mismatch is
177 * found. May read in more characters.
179 static int
180 TraverseMap(ptr, ch, val)
181 XmapNode *ptr;
182 CStr *ch;
183 XmapVal *val;
185 Char tch;
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 */
195 *(ch->buf) = tch;
196 return (TraverseMap(ptr->next, ch, val));
198 else {
199 *val = ptr->val;
200 if (ptr->type != XK_CMD)
201 *(ch->buf) = '\0';
202 return ptr->type;
205 else {
206 /* no match found here */
207 if (ptr->sibling) {
208 /* try next sibling */
209 return (TraverseMap(ptr->sibling, ch, val));
211 else {
212 /* no next sibling -- mismatch */
213 val->str.buf = NULL;
214 val->str.len = 0;
215 return XK_STR;
220 void
221 AddXkey(Xkey, val, ntype)
222 CStr *Xkey;
223 XmapVal *val;
224 int ntype;
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;
234 if (ntype == XK_CMD && val->cmd == F_XKEY) {
235 xprintf(CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n"));
236 return;
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 */
243 /* Now recurse through Xmap */
244 (void) TryNode(Xmap, &cs, val, ntype);
245 return;
248 static int
249 TryNode(ptr, str, val, ntype)
250 XmapNode *ptr;
251 CStr *str;
252 XmapVal *val;
253 int ntype;
256 * Find a node that matches *string or allocate a new one
258 if (ptr->ch != *(str->buf)) {
259 XmapNode *xm;
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;
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;
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;
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;
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);
316 return (0);
319 void
320 ClearXkey(map, in)
321 KEYCMD *map;
322 CStr *in;
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);
332 DeleteXkey(Xkey)
333 CStr *Xkey;
335 if (Xkey->len == 0) {
336 xprintf(CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n"));
337 return (-1);
340 if (Xmap == NULL)
341 return (0);
343 (void) TryDeleteNode(&Xmap, Xkey);
344 return (0);
347 static int
348 TryDeleteNode(inptr, str)
349 XmapNode **inptr;
350 CStr *str;
352 XmapNode *ptr;
353 XmapNode *prev_ptr = NULL;
355 ptr = *inptr;
357 * Find a node that matches *string or allocate a new one
359 if (ptr->ch != *(str->buf)) {
360 XmapNode *xm;
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;
371 str->buf++;
372 str->len--;
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);
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);
395 else {
396 return (0);
400 /* PutFreeNode():
401 * Puts a tree of nodes onto free list using free(3).
403 static void
404 PutFreeNode(ptr)
405 XmapNode *ptr;
407 if (ptr == NULL)
408 return;
410 if (ptr->next != NULL) {
411 PutFreeNode(ptr->next);
412 ptr->next = NULL;
415 PutFreeNode(ptr->sibling);
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;
430 xfree((ptr_t) ptr);
434 /* GetFreeNode():
435 * Returns pointer to an XmapNode for ch.
437 static XmapNode *
438 GetFreeNode(ch)
439 CStr *ch;
441 XmapNode *ptr;
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);
454 /* PrintXKey():
455 * Print the binding associated with Xkey key.
456 * Print entire Xmap if null
458 void
459 PrintXkey(key)
460 CStr *key;
462 CStr cs;
464 if (key) {
465 cs.buf = key->buf;
466 cs.len = key->len;
468 else {
469 cs.buf = STRNULL;
470 cs.len = 0;
472 /* do nothing if Xmap is empty and null key specified */
473 if (Xmap == NULL && cs.len == 0)
474 return;
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;
483 /* Lookup():
484 * look for the string starting at node ptr.
485 * Print if last node
487 static int
488 Lookup(str, ptr, cnt)
489 CStr *str;
490 XmapNode *ptr;
491 int cnt;
493 int ncnt;
495 if (ptr == NULL)
496 return (-1); /* cannot have null ptr */
498 if (str->len == 0) {
499 /* no more chars in string. Enumerate from here. */
500 (void) Enumerate(ptr, cnt);
501 return (0);
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));
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);
526 else
527 return (-1);/* mismatch -- string still has chars */
530 else {
531 /* no match found try sibling */
532 if (ptr->sibling)
533 return (Lookup(str, ptr->sibling, cnt));
534 else
535 return (-1);
540 static int
541 Enumerate(ptr, cnt)
542 XmapNode *ptr;
543 int cnt;
545 int ncnt;
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);
556 if (ptr == NULL) {
557 #ifdef DEBUG_EDIT
558 xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
559 #endif
560 return (-1);
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);
573 else
574 (void) Enumerate(ptr->next, ncnt + 1);
576 /* go to sibling if there is one */
577 if (ptr->sibling)
578 (void) Enumerate(ptr->sibling, cnt);
579 return (0);
583 /* PrintOne():
584 * Print the specified key and its associated
585 * function specified by val
588 printOne(key, val, ntype)
589 CStr *key;
590 XmapVal *val;
591 int ntype;
593 struct KeyFuncs *fp;
594 unsigned char unparsbuf[200];
595 static char *fmt = "%s\n";
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;
614 else
615 xprintf(fmt, key, CGETS(9, 7, "no input"));
616 return (0);
619 static int
620 unparsech(cnt, ch)
621 int cnt;
622 Char *ch;
624 if (ch == 0) {
625 printbuf[cnt++] = '^';
626 printbuf[cnt] = '@';
627 return cnt;
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'))
640 printbuf[cnt++] = '^';
641 printbuf[cnt] = '?';
643 else if (Isupper(_toebcdic[_toascii[*ch]|0100])
644 || strchr("@[\\]^_", _toebcdic[_toascii[*ch]|0100]) != NULL)
646 printbuf[cnt++] = '^';
647 printbuf[cnt] = _toebcdic[_toascii[*ch]|0100];
649 else
651 printbuf[cnt++] = '\\';
652 printbuf[cnt++] = ((*ch >> 6) & 7) + '0';
653 printbuf[cnt++] = ((*ch >> 3) & 7) + '0';
654 printbuf[cnt] = (*ch & 7) + '0';
656 #endif
658 else if (*ch == '^') {
659 printbuf[cnt++] = '\\';
660 printbuf[cnt] = '^';
662 else if (*ch == '\\') {
663 printbuf[cnt++] = '\\';
664 printbuf[cnt] = '\\';
666 else if (*ch == ' ' || (Isprint(*ch) && !Isspace(*ch))) {
667 printbuf[cnt] = *ch;
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';
675 return cnt;
679 parseescape(ptr)
680 const Char **ptr;
682 const Char *p;
683 Char c;
685 p = *ptr;
687 if ((p[1] & CHAR) == 0) {
688 xprintf(CGETS(9, 8, "Something must follow: %c\n"), *p);
689 return -1;
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':
730 register int cnt, val, ch;
732 for (cnt = 0, val = 0; cnt < 3; cnt++) {
733 ch = *p++ & CHAR;
734 if (ch < '0' || ch > '7') {
735 p--;
736 break;
738 val = (val << 3) | (ch - '0');
740 if ((val & 0xffffff00) != 0) {
741 xprintf(CGETS(9, 9,
742 "Octal constant does not fit in a char.\n"));
743 return 0;
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;
753 break;
754 default:
755 c = *p;
756 break;
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
771 else
772 c = *p;
773 *ptr = p;
774 return (c);
778 unsigned char *
779 unparsestring(str, buf, sep)
780 CStr *str;
781 unsigned char *buf;
782 Char *sep;
784 unsigned char *b;
785 Char p;
786 int l;
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 */
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)
809 *b++ = '^';
810 *b++ = (_toascii[p] == '\177') ? '?' : _toebcdic[_toascii[p]|0100];
812 else
814 *b++ = '\\';
815 *b++ = ((p >> 6) & 7) + '0';
816 *b++ = ((p >> 3) & 7) + '0';
817 *b++ = (p & 7) + '0';
819 #endif
821 else if (p == '^' || p == '\\') {
822 *b++ = '\\';
823 *b++ = (unsigned char) p;
825 else if (p == ' ' || (Isprint(p) && !Isspace(p))) {
826 *b++ = (unsigned char) p;
828 else {
829 *b++ = '\\';
830 *b++ = ((p >> 6) & 7) + '0';
831 *b++ = ((p >> 3) & 7) + '0';
832 *b++ = (p & 7) + '0';
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 */