| 1 | /* $Header: /p/tcsh/cvsroot/tcsh/ed.xmap.c,v 3.37 2009/06/25 21:15:37 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("$tcsh: ed.xmap.c,v 3.37 2009/06/25 21:15:37 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 | |
| 116 | |
| 117 | /* Some declarations of procedures */ |
| 118 | static int TraverseMap (XmapNode *, CStr *, XmapVal *); |
| 119 | static int TryNode (XmapNode *, CStr *, XmapVal *, int); |
| 120 | static XmapNode *GetFreeNode (CStr *); |
| 121 | static void PutFreeNode (XmapNode *); |
| 122 | static int TryDeleteNode (XmapNode **, CStr *); |
| 123 | static int Lookup (struct Strbuf *, const CStr *, |
| 124 | const XmapNode *); |
| 125 | static void Enumerate (struct Strbuf *, const XmapNode *); |
| 126 | static void unparsech (struct Strbuf *, Char); |
| 127 | |
| 128 | |
| 129 | XmapVal * |
| 130 | XmapCmd(int cmd) |
| 131 | { |
| 132 | static XmapVal xm; |
| 133 | xm.cmd = (KEYCMD) cmd; |
| 134 | return &xm; |
| 135 | } |
| 136 | |
| 137 | XmapVal * |
| 138 | XmapStr(CStr *str) |
| 139 | { |
| 140 | static XmapVal xm; |
| 141 | xm.str.len = str->len; |
| 142 | xm.str.buf = str->buf; |
| 143 | return &xm; |
| 144 | } |
| 145 | |
| 146 | /* ResetXmap(): |
| 147 | * Takes all nodes on Xmap and puts them on free list. Then |
| 148 | * initializes Xmap with arrow keys |
| 149 | */ |
| 150 | void |
| 151 | ResetXmap(void) |
| 152 | { |
| 153 | PutFreeNode(Xmap); |
| 154 | Xmap = NULL; |
| 155 | |
| 156 | DefaultArrowKeys(); |
| 157 | return; |
| 158 | } |
| 159 | |
| 160 | |
| 161 | /* GetXkey(): |
| 162 | * Calls the recursive function with entry point Xmap |
| 163 | */ |
| 164 | int |
| 165 | GetXkey(CStr *ch, XmapVal *val) |
| 166 | { |
| 167 | return (TraverseMap(Xmap, ch, val)); |
| 168 | } |
| 169 | |
| 170 | /* TraverseMap(): |
| 171 | * recursively traverses node in tree until match or mismatch is |
| 172 | * found. May read in more characters. |
| 173 | */ |
| 174 | static int |
| 175 | TraverseMap(XmapNode *ptr, CStr *ch, XmapVal *val) |
| 176 | { |
| 177 | Char tch; |
| 178 | |
| 179 | if (ptr->ch == *(ch->buf)) { |
| 180 | /* match found */ |
| 181 | if (ptr->next) { |
| 182 | /* Xkey not complete so get next char */ |
| 183 | if (GetNextChar(&tch) != 1) { /* if EOF or error */ |
| 184 | val->cmd = F_SEND_EOF; |
| 185 | return XK_CMD;/* PWP: Pretend we just read an end-of-file */ |
| 186 | } |
| 187 | *(ch->buf) = tch; |
| 188 | return (TraverseMap(ptr->next, ch, val)); |
| 189 | } |
| 190 | else { |
| 191 | *val = ptr->val; |
| 192 | if (ptr->type != XK_CMD) |
| 193 | *(ch->buf) = '\0'; |
| 194 | return ptr->type; |
| 195 | } |
| 196 | } |
| 197 | else { |
| 198 | /* no match found here */ |
| 199 | if (ptr->sibling) { |
| 200 | /* try next sibling */ |
| 201 | return (TraverseMap(ptr->sibling, ch, val)); |
| 202 | } |
| 203 | else { |
| 204 | /* no next sibling -- mismatch */ |
| 205 | val->str.buf = NULL; |
| 206 | val->str.len = 0; |
| 207 | return XK_STR; |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | void |
| 213 | AddXkey(const CStr *Xkey, XmapVal *val, int ntype) |
| 214 | { |
| 215 | CStr cs; |
| 216 | cs.buf = Xkey->buf; |
| 217 | cs.len = Xkey->len; |
| 218 | if (Xkey->len == 0) { |
| 219 | xprintf("%s", CGETS(9, 1, "AddXkey: Null extended-key not allowed.\n")); |
| 220 | return; |
| 221 | } |
| 222 | |
| 223 | if (ntype == XK_CMD && val->cmd == F_XKEY) { |
| 224 | xprintf("%s", |
| 225 | CGETS(9, 2, "AddXkey: sequence-lead-in command not allowed\n")); |
| 226 | return; |
| 227 | } |
| 228 | |
| 229 | if (Xmap == NULL) |
| 230 | /* tree is initially empty. Set up new node to match Xkey[0] */ |
| 231 | Xmap = GetFreeNode(&cs); /* it is properly initialized */ |
| 232 | |
| 233 | /* Now recurse through Xmap */ |
| 234 | (void) TryNode(Xmap, &cs, val, ntype); |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | static int |
| 239 | TryNode(XmapNode *ptr, CStr *str, XmapVal *val, int ntype) |
| 240 | { |
| 241 | /* |
| 242 | * Find a node that matches *string or allocate a new one |
| 243 | */ |
| 244 | if (ptr->ch != *(str->buf)) { |
| 245 | XmapNode *xm; |
| 246 | |
| 247 | for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) |
| 248 | if (xm->sibling->ch == *(str->buf)) |
| 249 | break; |
| 250 | if (xm->sibling == NULL) |
| 251 | xm->sibling = GetFreeNode(str); /* setup new node */ |
| 252 | ptr = xm->sibling; |
| 253 | } |
| 254 | |
| 255 | str->buf++; |
| 256 | str->len--; |
| 257 | if (str->len == 0) { |
| 258 | size_t len; |
| 259 | |
| 260 | /* we're there */ |
| 261 | if (ptr->next != NULL) { |
| 262 | PutFreeNode(ptr->next); /* lose longer Xkeys with this prefix */ |
| 263 | ptr->next = NULL; |
| 264 | } |
| 265 | |
| 266 | switch (ptr->type) { |
| 267 | case XK_STR: |
| 268 | case XK_EXE: |
| 269 | xfree(ptr->val.str.buf); |
| 270 | ptr->val.str.len = 0; |
| 271 | break; |
| 272 | case XK_NOD: |
| 273 | case XK_CMD: |
| 274 | break; |
| 275 | default: |
| 276 | abort(); |
| 277 | break; |
| 278 | } |
| 279 | |
| 280 | switch (ptr->type = ntype) { |
| 281 | case XK_CMD: |
| 282 | ptr->val = *val; |
| 283 | break; |
| 284 | case XK_STR: |
| 285 | case XK_EXE: |
| 286 | ptr->val.str.len = val->str.len; |
| 287 | len = (val->str.len + 1) * sizeof(*ptr->val.str.buf); |
| 288 | ptr->val.str.buf = xmalloc(len); |
| 289 | (void) memcpy(ptr->val.str.buf, val->str.buf, len); |
| 290 | break; |
| 291 | default: |
| 292 | abort(); |
| 293 | break; |
| 294 | } |
| 295 | } |
| 296 | else { |
| 297 | /* still more chars to go */ |
| 298 | if (ptr->next == NULL) |
| 299 | ptr->next = GetFreeNode(str); /* setup new node */ |
| 300 | (void) TryNode(ptr->next, str, val, ntype); |
| 301 | } |
| 302 | return (0); |
| 303 | } |
| 304 | |
| 305 | void |
| 306 | ClearXkey(KEYCMD *map, const CStr *in) |
| 307 | { |
| 308 | unsigned char c = (unsigned char) *(in->buf); |
| 309 | if ((map[c] == F_XKEY) && |
| 310 | ((map == CcKeyMap && CcAltMap[c] != F_XKEY) || |
| 311 | (map == CcAltMap && CcKeyMap[c] != F_XKEY))) |
| 312 | (void) DeleteXkey(in); |
| 313 | } |
| 314 | |
| 315 | int |
| 316 | DeleteXkey(const CStr *Xkey) |
| 317 | { |
| 318 | CStr s; |
| 319 | |
| 320 | s = *Xkey; |
| 321 | if (s.len == 0) { |
| 322 | xprintf("%s", |
| 323 | CGETS(9, 3, "DeleteXkey: Null extended-key not allowed.\n")); |
| 324 | return (-1); |
| 325 | } |
| 326 | |
| 327 | if (Xmap == NULL) |
| 328 | return (0); |
| 329 | |
| 330 | (void) TryDeleteNode(&Xmap, &s); |
| 331 | return (0); |
| 332 | } |
| 333 | |
| 334 | /* Destroys str */ |
| 335 | static int |
| 336 | TryDeleteNode(XmapNode **inptr, CStr *str) |
| 337 | { |
| 338 | XmapNode *ptr; |
| 339 | |
| 340 | ptr = *inptr; |
| 341 | /* |
| 342 | * Find a node that matches *string or allocate a new one |
| 343 | */ |
| 344 | if (ptr->ch != *(str->buf)) { |
| 345 | XmapNode *xm; |
| 346 | |
| 347 | for (xm = ptr; xm->sibling != NULL; xm = xm->sibling) |
| 348 | if (xm->sibling->ch == *(str->buf)) |
| 349 | break; |
| 350 | if (xm->sibling == NULL) |
| 351 | return (0); |
| 352 | inptr = &xm->sibling; |
| 353 | ptr = xm->sibling; |
| 354 | } |
| 355 | |
| 356 | str->buf++; |
| 357 | str->len--; |
| 358 | |
| 359 | if (str->len == 0) { |
| 360 | /* we're there */ |
| 361 | *inptr = ptr->sibling; |
| 362 | ptr->sibling = NULL; |
| 363 | PutFreeNode(ptr); |
| 364 | return (1); |
| 365 | } |
| 366 | else if (ptr->next != NULL && TryDeleteNode(&ptr->next, str) == 1) { |
| 367 | if (ptr->next != NULL) |
| 368 | return (0); |
| 369 | *inptr = ptr->sibling; |
| 370 | ptr->sibling = NULL; |
| 371 | PutFreeNode(ptr); |
| 372 | return (1); |
| 373 | } |
| 374 | else { |
| 375 | return (0); |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | /* PutFreeNode(): |
| 380 | * Puts a tree of nodes onto free list using free(3). |
| 381 | */ |
| 382 | static void |
| 383 | PutFreeNode(XmapNode *ptr) |
| 384 | { |
| 385 | if (ptr == NULL) |
| 386 | return; |
| 387 | |
| 388 | if (ptr->next != NULL) { |
| 389 | PutFreeNode(ptr->next); |
| 390 | ptr->next = NULL; |
| 391 | } |
| 392 | |
| 393 | PutFreeNode(ptr->sibling); |
| 394 | |
| 395 | switch (ptr->type) { |
| 396 | case XK_CMD: |
| 397 | case XK_NOD: |
| 398 | break; |
| 399 | case XK_EXE: |
| 400 | case XK_STR: |
| 401 | xfree(ptr->val.str.buf); |
| 402 | break; |
| 403 | default: |
| 404 | abort(); |
| 405 | break; |
| 406 | } |
| 407 | xfree(ptr); |
| 408 | } |
| 409 | |
| 410 | |
| 411 | /* GetFreeNode(): |
| 412 | * Returns pointer to an XmapNode for ch. |
| 413 | */ |
| 414 | static XmapNode * |
| 415 | GetFreeNode(CStr *ch) |
| 416 | { |
| 417 | XmapNode *ptr; |
| 418 | |
| 419 | ptr = xmalloc(sizeof(XmapNode)); |
| 420 | ptr->ch = ch->buf[0]; |
| 421 | ptr->type = XK_NOD; |
| 422 | ptr->val.str.buf = NULL; |
| 423 | ptr->val.str.len = 0; |
| 424 | ptr->next = NULL; |
| 425 | ptr->sibling = NULL; |
| 426 | return (ptr); |
| 427 | } |
| 428 | |
| 429 | |
| 430 | /* PrintXKey(): |
| 431 | * Print the binding associated with Xkey key. |
| 432 | * Print entire Xmap if null |
| 433 | */ |
| 434 | void |
| 435 | PrintXkey(const CStr *key) |
| 436 | { |
| 437 | struct Strbuf buf = Strbuf_INIT; |
| 438 | CStr cs; |
| 439 | |
| 440 | if (key) { |
| 441 | cs.buf = key->buf; |
| 442 | cs.len = key->len; |
| 443 | } |
| 444 | else { |
| 445 | cs.buf = STRNULL; |
| 446 | cs.len = 0; |
| 447 | } |
| 448 | /* do nothing if Xmap is empty and null key specified */ |
| 449 | if (Xmap == NULL && cs.len == 0) |
| 450 | return; |
| 451 | |
| 452 | Strbuf_append1(&buf, '"'); |
| 453 | cleanup_push(&buf, Strbuf_cleanup); |
| 454 | if (Lookup(&buf, &cs, Xmap) <= -1) |
| 455 | /* key is not bound */ |
| 456 | xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf); |
| 457 | cleanup_until(&buf); |
| 458 | } |
| 459 | |
| 460 | /* Lookup(): |
| 461 | * look for the string starting at node ptr. |
| 462 | * Print if last node |
| 463 | */ |
| 464 | static int |
| 465 | Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr) |
| 466 | { |
| 467 | if (ptr == NULL) |
| 468 | return (-1); /* cannot have null ptr */ |
| 469 | |
| 470 | if (str->len == 0) { |
| 471 | /* no more chars in string. Enumerate from here. */ |
| 472 | Enumerate(buf, ptr); |
| 473 | return (0); |
| 474 | } |
| 475 | else { |
| 476 | /* If match put this char into buf. Recurse */ |
| 477 | if (ptr->ch == *(str->buf)) { |
| 478 | /* match found */ |
| 479 | unparsech(buf, ptr->ch); |
| 480 | if (ptr->next != NULL) { |
| 481 | /* not yet at leaf */ |
| 482 | CStr tstr; |
| 483 | tstr.buf = str->buf + 1; |
| 484 | tstr.len = str->len - 1; |
| 485 | return (Lookup(buf, &tstr, ptr->next)); |
| 486 | } |
| 487 | else { |
| 488 | /* next node is null so key should be complete */ |
| 489 | if (str->len == 1) { |
| 490 | Strbuf_append1(buf, '"'); |
| 491 | Strbuf_terminate(buf); |
| 492 | printOne(buf->s, &ptr->val, ptr->type); |
| 493 | return (0); |
| 494 | } |
| 495 | else |
| 496 | return (-1);/* mismatch -- string still has chars */ |
| 497 | } |
| 498 | } |
| 499 | else { |
| 500 | /* no match found try sibling */ |
| 501 | if (ptr->sibling) |
| 502 | return (Lookup(buf, str, ptr->sibling)); |
| 503 | else |
| 504 | return (-1); |
| 505 | } |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | static void |
| 510 | Enumerate(struct Strbuf *buf, const XmapNode *ptr) |
| 511 | { |
| 512 | size_t old_len; |
| 513 | |
| 514 | if (ptr == NULL) { |
| 515 | #ifdef DEBUG_EDIT |
| 516 | xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!")); |
| 517 | #endif |
| 518 | return; |
| 519 | } |
| 520 | |
| 521 | old_len = buf->len; |
| 522 | unparsech(buf, ptr->ch); /* put this char at end of string */ |
| 523 | if (ptr->next == NULL) { |
| 524 | /* print this Xkey and function */ |
| 525 | Strbuf_append1(buf, '"'); |
| 526 | Strbuf_terminate(buf); |
| 527 | printOne(buf->s, &ptr->val, ptr->type); |
| 528 | } |
| 529 | else |
| 530 | Enumerate(buf, ptr->next); |
| 531 | |
| 532 | /* go to sibling if there is one */ |
| 533 | if (ptr->sibling) { |
| 534 | buf->len = old_len; |
| 535 | Enumerate(buf, ptr->sibling); |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | |
| 540 | /* PrintOne(): |
| 541 | * Print the specified key and its associated |
| 542 | * function specified by val |
| 543 | */ |
| 544 | void |
| 545 | printOne(const Char *key, const XmapVal *val, int ntype) |
| 546 | { |
| 547 | struct KeyFuncs *fp; |
| 548 | static const char *fmt = "%s\n"; |
| 549 | |
| 550 | xprintf("%-15S-> ", key); |
| 551 | if (val != NULL) |
| 552 | switch (ntype) { |
| 553 | case XK_STR: |
| 554 | case XK_EXE: { |
| 555 | unsigned char *p; |
| 556 | |
| 557 | p = unparsestring(&val->str, ntype == XK_STR ? STRQQ : STRBB); |
| 558 | cleanup_push(p, xfree); |
| 559 | xprintf(fmt, p); |
| 560 | cleanup_until(p); |
| 561 | break; |
| 562 | } |
| 563 | case XK_CMD: |
| 564 | for (fp = FuncNames; fp->name; fp++) |
| 565 | if (val->cmd == fp->func) |
| 566 | xprintf(fmt, fp->name); |
| 567 | break; |
| 568 | default: |
| 569 | abort(); |
| 570 | break; |
| 571 | } |
| 572 | else |
| 573 | xprintf(fmt, CGETS(9, 7, "no input")); |
| 574 | } |
| 575 | |
| 576 | static void |
| 577 | unparsech(struct Strbuf *buf, Char ch) |
| 578 | { |
| 579 | if (ch == 0) { |
| 580 | Strbuf_append1(buf, '^'); |
| 581 | Strbuf_append1(buf, '@'); |
| 582 | } |
| 583 | else if (Iscntrl(ch)) { |
| 584 | Strbuf_append1(buf, '^'); |
| 585 | if (ch == CTL_ESC('\177')) |
| 586 | Strbuf_append1(buf, '?'); |
| 587 | else |
| 588 | #ifdef IS_ASCII |
| 589 | Strbuf_append1(buf, ch | 0100); |
| 590 | #else |
| 591 | Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]); |
| 592 | #endif |
| 593 | } |
| 594 | else if (ch == '^') { |
| 595 | Strbuf_append1(buf, '\\'); |
| 596 | Strbuf_append1(buf, '^'); |
| 597 | } else if (ch == '\\') { |
| 598 | Strbuf_append1(buf, '\\'); |
| 599 | Strbuf_append1(buf, '\\'); |
| 600 | } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) { |
| 601 | Strbuf_append1(buf, ch); |
| 602 | } |
| 603 | else { |
| 604 | Strbuf_append1(buf, '\\'); |
| 605 | Strbuf_append1(buf, ((ch >> 6) & 7) + '0'); |
| 606 | Strbuf_append1(buf, ((ch >> 3) & 7) + '0'); |
| 607 | Strbuf_append1(buf, (ch & 7) + '0'); |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | eChar |
| 612 | parseescape(const Char **ptr) |
| 613 | { |
| 614 | const Char *p; |
| 615 | Char c; |
| 616 | |
| 617 | p = *ptr; |
| 618 | |
| 619 | if ((p[1] & CHAR) == 0) { |
| 620 | xprintf(CGETS(9, 8, "Something must follow: %c\n"), (char)*p); |
| 621 | return CHAR_ERR; |
| 622 | } |
| 623 | if ((*p & CHAR) == '\\') { |
| 624 | p++; |
| 625 | switch (*p & CHAR) { |
| 626 | case 'a': |
| 627 | c = CTL_ESC('\007'); /* Bell */ |
| 628 | break; |
| 629 | case 'b': |
| 630 | c = CTL_ESC('\010'); /* Backspace */ |
| 631 | break; |
| 632 | case 'e': |
| 633 | c = CTL_ESC('\033'); /* Escape */ |
| 634 | break; |
| 635 | case 'f': |
| 636 | c = CTL_ESC('\014'); /* Form Feed */ |
| 637 | break; |
| 638 | case 'n': |
| 639 | c = CTL_ESC('\012'); /* New Line */ |
| 640 | break; |
| 641 | case 'r': |
| 642 | c = CTL_ESC('\015'); /* Carriage Return */ |
| 643 | break; |
| 644 | case 't': |
| 645 | c = CTL_ESC('\011'); /* Horizontal Tab */ |
| 646 | break; |
| 647 | case 'v': |
| 648 | c = CTL_ESC('\013'); /* Vertical Tab */ |
| 649 | break; |
| 650 | case '\\': |
| 651 | c = '\\'; |
| 652 | break; |
| 653 | case '0': |
| 654 | case '1': |
| 655 | case '2': |
| 656 | case '3': |
| 657 | case '4': |
| 658 | case '5': |
| 659 | case '6': |
| 660 | case '7': |
| 661 | { |
| 662 | int cnt, val; |
| 663 | Char ch; |
| 664 | |
| 665 | for (cnt = 0, val = 0; cnt < 3; cnt++) { |
| 666 | ch = *p++ & CHAR; |
| 667 | if (ch < '0' || ch > '7') { |
| 668 | p--; |
| 669 | break; |
| 670 | } |
| 671 | val = (val << 3) | (ch - '0'); |
| 672 | } |
| 673 | if ((val & ~0xff) != 0) { |
| 674 | xprintf("%s", CGETS(9, 9, |
| 675 | "Octal constant does not fit in a char.\n")); |
| 676 | return 0; |
| 677 | } |
| 678 | #ifndef IS_ASCII |
| 679 | if (CTL_ESC(val) != val && adrof(STRwarnebcdic)) |
| 680 | xprintf(/*CGETS(9, 9, no NLS-String yet!*/ |
| 681 | "Warning: Octal constant \\%3.3o is interpreted as EBCDIC value.\n", val/*)*/); |
| 682 | #endif |
| 683 | c = (Char) val; |
| 684 | --p; |
| 685 | } |
| 686 | break; |
| 687 | default: |
| 688 | c = *p; |
| 689 | break; |
| 690 | } |
| 691 | } |
| 692 | else if ((*p & CHAR) == '^' && (Isalpha(p[1] & CHAR) || |
| 693 | strchr("@^_?\\|[{]}", p[1] & CHAR))) { |
| 694 | p++; |
| 695 | #ifdef IS_ASCII |
| 696 | c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : ((*p & CHAR) & 0237); |
| 697 | #else |
| 698 | c = ((*p & CHAR) == '?') ? CTL_ESC('\177') : _toebcdic[_toascii[*p & CHAR] & 0237]; |
| 699 | if (adrof(STRwarnebcdic)) |
| 700 | xprintf(/*CGETS(9, 9, no NLS-String yet!*/ |
| 701 | "Warning: Control character ^%c may be interpreted differently in EBCDIC.\n", *p & CHAR /*)*/); |
| 702 | #endif |
| 703 | } |
| 704 | else |
| 705 | c = *p; |
| 706 | *ptr = p; |
| 707 | return (c); |
| 708 | } |
| 709 | |
| 710 | |
| 711 | unsigned char * |
| 712 | unparsestring(const CStr *str, const Char *sep) |
| 713 | { |
| 714 | unsigned char *buf, *b; |
| 715 | Char p; |
| 716 | int l; |
| 717 | |
| 718 | /* Worst-case is "\uuu" or result of wctomb() for each char from str */ |
| 719 | buf = xmalloc((str->len + 1) * max(4, MB_LEN_MAX)); |
| 720 | b = buf; |
| 721 | if (sep[0]) |
| 722 | #ifndef WINNT_NATIVE |
| 723 | *b++ = sep[0]; |
| 724 | #else /* WINNT_NATIVE */ |
| 725 | *b++ = CHAR & sep[0]; |
| 726 | #endif /* !WINNT_NATIVE */ |
| 727 | |
| 728 | for (l = 0; l < str->len; l++) { |
| 729 | p = str->buf[l]; |
| 730 | if (Iscntrl(p)) { |
| 731 | *b++ = '^'; |
| 732 | if (p == CTL_ESC('\177')) |
| 733 | *b++ = '?'; |
| 734 | else |
| 735 | #ifdef IS_ASCII |
| 736 | *b++ = (unsigned char) (p | 0100); |
| 737 | #else |
| 738 | *b++ = _toebcdic[_toascii[p]|0100]; |
| 739 | #endif |
| 740 | } |
| 741 | else if (p == '^' || p == '\\') { |
| 742 | *b++ = '\\'; |
| 743 | *b++ = (unsigned char) p; |
| 744 | } |
| 745 | else if (p == ' ' || (Isprint(p) && !Isspace(p))) |
| 746 | b += one_wctomb((char *)b, p & CHAR); |
| 747 | else { |
| 748 | *b++ = '\\'; |
| 749 | *b++ = ((p >> 6) & 7) + '0'; |
| 750 | *b++ = ((p >> 3) & 7) + '0'; |
| 751 | *b++ = (p & 7) + '0'; |
| 752 | } |
| 753 | } |
| 754 | if (sep[0] && sep[1]) |
| 755 | #ifndef WINNT_NATIVE |
| 756 | *b++ = sep[1]; |
| 757 | #else /* WINNT_NATIVE */ |
| 758 | *b++ = CHAR & sep[1]; |
| 759 | #endif /* !WINNT_NATIVE */ |
| 760 | *b++ = 0; |
| 761 | return buf; /* should check for overflow */ |
| 762 | } |