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 * 126 XmapCmd(int cmd) 127 { 128 static XmapVal xm; 129 xm.cmd = (KEYCMD) cmd; 130 return &xm; 131 } 132 133 XmapVal * 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 * 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