1 /* $OpenBSD: edit.c,v 1.33 2007/08/02 10:50:25 fgsch Exp $ */ 2 3 /* 4 * Command line editing - common code 5 * 6 */ 7 8 #include "config.h" 9 #ifdef EDIT 10 11 #include "sh.h" 12 #include "tty.h" 13 #define EXTERN 14 #include "edit.h" 15 #undef EXTERN 16 #include <sys/ioctl.h> 17 #include <ctype.h> 18 #include <libgen.h> 19 #include <sys/stat.h> 20 21 22 static void x_sigwinch(int); 23 static volatile sig_atomic_t got_sigwinch; 24 static void check_sigwinch(void); 25 26 static int x_file_glob(int, const char *, int, char ***); 27 static int x_command_glob(int, const char *, int, char ***); 28 static int x_locate_word(const char *, int, int, int *, int *); 29 30 31 /* Called from main */ 32 void 33 x_init(void) 34 { 35 /* set to -2 to force initial binding */ 36 edchars.erase = edchars.kill = edchars.intr = edchars.quit = 37 edchars.eof = -2; 38 /* default value for deficient systems */ 39 edchars.werase = 027; /* ^W */ 40 41 if (setsig(&sigtraps[SIGWINCH], x_sigwinch, SS_RESTORE_ORIG|SS_SHTRAP)) 42 sigtraps[SIGWINCH].flags |= TF_SHELL_USES; 43 got_sigwinch = 1; /* force initial check */ 44 check_sigwinch(); 45 46 #ifdef EMACS 47 x_init_emacs(); 48 #endif /* EMACS */ 49 } 50 51 /* ARGSUSED */ 52 static void 53 x_sigwinch(int sig) 54 { 55 got_sigwinch = 1; 56 } 57 58 static void 59 check_sigwinch(void) 60 { 61 if (got_sigwinch) { 62 struct winsize ws; 63 64 got_sigwinch = 0; 65 if (procpid == kshpid && ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) { 66 struct tbl *vp; 67 68 /* Do NOT export COLUMNS/LINES. Many applications 69 * check COLUMNS/LINES before checking ws.ws_col/row, 70 * so if the app is started with C/L in the environ 71 * and the window is then resized, the app won't 72 * see the change cause the environ doesn't change. 73 */ 74 if (ws.ws_col) { 75 x_cols = ws.ws_col < MIN_COLS ? MIN_COLS : 76 ws.ws_col; 77 78 if ((vp = typeset("COLUMNS", 0, 0, 0, 0))) 79 setint(vp, (long) ws.ws_col); 80 } 81 if (ws.ws_row && (vp = typeset("LINES", 0, 0, 0, 0))) 82 setint(vp, (long) ws.ws_row); 83 } 84 } 85 } 86 87 /* 88 * read an edited command line 89 */ 90 int 91 x_read(char *buf, size_t len) 92 { 93 int i; 94 95 x_mode(true); 96 #ifdef EMACS 97 if (Flag(FEMACS) || Flag(FGMACS)) 98 i = x_emacs(buf, len); 99 else 100 #endif 101 #ifdef VI 102 if (Flag(FVI)) 103 i = x_vi(buf, len); 104 else 105 #endif 106 i = -1; /* internal error */ 107 x_mode(false); 108 check_sigwinch(); 109 return i; 110 } 111 112 /* tty I/O */ 113 114 int 115 x_getc(void) 116 { 117 char c; 118 int n; 119 120 while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR) 121 if (trap) { 122 x_mode(false); 123 runtraps(0); 124 x_mode(true); 125 } 126 if (n != 1) 127 return -1; 128 return (int) (unsigned char) c; 129 } 130 131 void 132 x_flush(void) 133 { 134 shf_flush(shl_out); 135 } 136 137 void 138 x_putc(int c) 139 { 140 shf_putc(c, shl_out); 141 } 142 143 void 144 x_puts(const char *s) 145 { 146 while (*s != 0) 147 shf_putc(*s++, shl_out); 148 } 149 150 bool 151 x_mode(bool onoff) 152 { 153 static bool x_cur_mode; 154 bool prev; 155 156 if (x_cur_mode == onoff) 157 return x_cur_mode; 158 prev = x_cur_mode; 159 x_cur_mode = onoff; 160 161 if (onoff) { 162 struct termios cb; 163 X_chars oldchars; 164 165 oldchars = edchars; 166 cb = tty_state; 167 168 edchars.erase = cb.c_cc[VERASE]; 169 edchars.kill = cb.c_cc[VKILL]; 170 edchars.intr = cb.c_cc[VINTR]; 171 edchars.quit = cb.c_cc[VQUIT]; 172 edchars.eof = cb.c_cc[VEOF]; 173 edchars.werase = cb.c_cc[VWERASE]; 174 cb.c_iflag &= ~(INLCR|ICRNL); 175 cb.c_lflag &= ~(ISIG|ICANON|ECHO); 176 /* osf/1 processes lnext when ~icanon */ 177 cb.c_cc[VLNEXT] = _POSIX_VDISABLE; 178 /* sunos 4.1.x & osf/1 processes discard(flush) when ~icanon */ 179 cb.c_cc[VDISCARD] = _POSIX_VDISABLE; 180 cb.c_cc[VTIME] = 0; 181 cb.c_cc[VMIN] = 1; 182 183 tcsetattr(tty_fd, TCSADRAIN, &cb); 184 185 /* Convert unset values to internal `unset' value */ 186 if (edchars.erase == _POSIX_VDISABLE) 187 edchars.erase = -1; 188 if (edchars.kill == _POSIX_VDISABLE) 189 edchars.kill = -1; 190 if (edchars.intr == _POSIX_VDISABLE) 191 edchars.intr = -1; 192 if (edchars.quit == _POSIX_VDISABLE) 193 edchars.quit = -1; 194 if (edchars.eof == _POSIX_VDISABLE) 195 edchars.eof = -1; 196 if (edchars.werase == _POSIX_VDISABLE) 197 edchars.werase = -1; 198 if (memcmp(&edchars, &oldchars, sizeof(edchars)) != 0) { 199 #ifdef EMACS 200 x_emacs_keys(&edchars); 201 #endif 202 } 203 } else { 204 tcsetattr(tty_fd, TCSADRAIN, &tty_state); 205 } 206 207 return prev; 208 } 209 210 void 211 set_editmode(const char *ed) 212 { 213 static const enum sh_flag edit_flags[] = { 214 #ifdef EMACS 215 FEMACS, FGMACS, 216 #endif 217 #ifdef VI 218 FVI, 219 #endif 220 }; 221 char *rcp; 222 int i; 223 224 if ((rcp = strrchr(ed, '/'))) 225 ed = ++rcp; 226 for (i = 0; i < NELEM(edit_flags); i++) 227 if (strstr(ed, options[(int) edit_flags[i]].name)) { 228 change_flag(edit_flags[i], OF_SPECIAL, 1); 229 return; 230 } 231 } 232 233 /* ------------------------------------------------------------------------- */ 234 /* Misc common code for vi/emacs */ 235 236 /* Handle the commenting/uncommenting of a line. 237 * Returns: 238 * 1 if a carriage return is indicated (comment added) 239 * 0 if no return (comment removed) 240 * -1 if there is an error (not enough room for comment chars) 241 * If successful, *lenp contains the new length. Note: cursor should be 242 * moved to the start of the line after (un)commenting. 243 */ 244 int 245 x_do_comment(char *buf, int bsize, int *lenp) 246 { 247 int i, j; 248 int len = *lenp; 249 250 if (len == 0) 251 return 1; /* somewhat arbitrary - it's what at&t ksh does */ 252 253 /* Already commented? */ 254 if (buf[0] == '#') { 255 int saw_nl = 0; 256 257 for (j = 0, i = 1; i < len; i++) { 258 if (!saw_nl || buf[i] != '#') 259 buf[j++] = buf[i]; 260 saw_nl = buf[i] == '\n'; 261 } 262 *lenp = j; 263 return 0; 264 } else { 265 int n = 1; 266 267 /* See if there's room for the #'s - 1 per \n */ 268 for (i = 0; i < len; i++) 269 if (buf[i] == '\n') 270 n++; 271 if (len + n >= bsize) 272 return -1; 273 /* Now add them... */ 274 for (i = len, j = len + n; --i >= 0; ) { 275 if (buf[i] == '\n') 276 buf[--j] = '#'; 277 buf[--j] = buf[i]; 278 } 279 buf[0] = '#'; 280 *lenp += n; 281 return 1; 282 } 283 } 284 285 /* ------------------------------------------------------------------------- */ 286 /* Common file/command completion code for vi/emacs */ 287 288 289 static char *add_glob(const char *str, int slen); 290 static void glob_table(const char *pat, XPtrV *wp, struct table *tp); 291 static void glob_path(int flags, const char *pat, XPtrV *wp, 292 const char *path); 293 294 #if 0 /* not used... */ 295 int x_complete_word(const char *str, int slen, int is_command, 296 int *multiple, char **ret); 297 int 298 x_complete_word(const char *str, int slen, int is_command, int *multiple, 299 char **ret) 300 { 301 int nwords; 302 int prefix_len; 303 char **words; 304 305 nwords = (is_command ? x_command_glob : x_file_glob)(XCF_FULLPATH, 306 str, slen, &words); 307 *nwordsp = nwords; 308 if (nwords == 0) { 309 *ret = (char *) 0; 310 return -1; 311 } 312 313 prefix_len = x_longest_prefix(nwords, words); 314 *ret = str_nsave(words[0], prefix_len, ATEMP); 315 x_free_words(nwords, words); 316 return prefix_len; 317 } 318 #endif /* 0 */ 319 320 void 321 x_print_expansions(int nwords, char *const *words, int is_command) 322 { 323 int use_copy = 0; 324 int prefix_len; 325 XPtrV l; 326 327 /* Check if all matches are in the same directory (in this 328 * case, we want to omit the directory name) 329 */ 330 if (!is_command && 331 (prefix_len = x_longest_prefix(nwords, words)) > 0) { 332 int i; 333 334 /* Special case for 1 match (prefix is whole word) */ 335 if (nwords == 1) 336 prefix_len = x_basename(words[0], (char *) 0); 337 /* Any (non-trailing) slashes in non-common word suffixes? */ 338 for (i = 0; i < nwords; i++) 339 if (x_basename(words[i] + prefix_len, (char *) 0) > 340 prefix_len) 341 break; 342 /* All in same directory? */ 343 if (i == nwords) { 344 while (prefix_len > 0 && words[0][prefix_len - 1] != '/') 345 prefix_len--; 346 use_copy = 1; 347 XPinit(l, nwords + 1); 348 for (i = 0; i < nwords; i++) 349 XPput(l, words[i] + prefix_len); 350 XPput(l, (char *) 0); 351 } 352 } 353 354 /* 355 * Enumerate expansions 356 */ 357 x_putc('\r'); 358 x_putc('\n'); 359 pr_list(use_copy ? (char **) XPptrv(l) : words); 360 361 if (use_copy) 362 XPfree(l); /* not x_free_words() */ 363 } 364 365 /* 366 * Do file globbing: 367 * - appends * to (copy of) str if no globbing chars found 368 * - does expansion, checks for no match, etc. 369 * - sets *wordsp to array of matching strings 370 * - returns number of matching strings 371 */ 372 static int 373 x_file_glob(int flags, const char *str, int slen, char ***wordsp) 374 { 375 char *toglob; 376 char **words; 377 int nwords, i, idx, escaping; 378 XPtrV w; 379 struct source *s, *sold; 380 381 if (slen < 0) 382 return 0; 383 384 toglob = add_glob(str, slen); 385 386 /* remove all escaping backward slashes */ 387 escaping = 0; 388 for (i = 0, idx = 0; toglob[i]; i++) { 389 if (toglob[i] == '\\' && !escaping) { 390 escaping = 1; 391 continue; 392 } 393 394 toglob[idx] = toglob[i]; 395 idx++; 396 if (escaping) escaping = 0; 397 } 398 toglob[idx] = '\0'; 399 400 /* 401 * Convert "foo*" (toglob) to an array of strings (words) 402 */ 403 sold = source; 404 s = pushs(SWSTR, ATEMP); 405 s->start = s->str = toglob; 406 source = s; 407 if (yylex(ONEWORD) != LWORD) { 408 source = sold; 409 internal_errorf(0, "fileglob: substitute error"); 410 return 0; 411 } 412 source = sold; 413 XPinit(w, 32); 414 expand(yylval.cp, &w, DOGLOB|DOTILDE|DOMARKDIRS); 415 XPput(w, NULL); 416 words = (char **) XPclose(w); 417 418 for (nwords = 0; words[nwords]; nwords++) 419 ; 420 if (nwords == 1) { 421 struct stat statb; 422 423 /* Check if globbing failed (returned glob pattern), 424 * but be careful (E.g. toglob == "ab*" when the file 425 * "ab*" exists is not an error). 426 * Also, check for empty result - happens if we tried 427 * to glob something which evaluated to an empty 428 * string (e.g., "$FOO" when there is no FOO, etc). 429 */ 430 if ((strcmp(words[0], toglob) == 0 && 431 stat(words[0], &statb) < 0) || 432 words[0][0] == '\0') { 433 x_free_words(nwords, words); 434 words = NULL; 435 nwords = 0; 436 } 437 } 438 afree(toglob, ATEMP); 439 440 if (nwords) { 441 *wordsp = words; 442 } else if (words) { 443 x_free_words(nwords, words); 444 *wordsp = NULL; 445 } 446 447 return nwords; 448 } 449 450 /* Data structure used in x_command_glob() */ 451 struct path_order_info { 452 char *word; 453 int base; 454 int path_order; 455 }; 456 457 static int path_order_cmp(const void *aa, const void *bb); 458 459 /* Compare routine used in x_command_glob() */ 460 static int 461 path_order_cmp(const void *aa, const void *bb) 462 { 463 const struct path_order_info *a = (const struct path_order_info *) aa; 464 const struct path_order_info *b = (const struct path_order_info *) bb; 465 int t; 466 467 t = strcmp(a->word + a->base, b->word + b->base); 468 return t ? t : a->path_order - b->path_order; 469 } 470 471 static int 472 x_command_glob(int flags, const char *str, int slen, char ***wordsp) 473 { 474 char *toglob; 475 char *pat; 476 char *fpath; 477 int nwords; 478 XPtrV w; 479 struct block *l; 480 481 if (slen < 0) 482 return 0; 483 484 toglob = add_glob(str, slen); 485 486 /* Convert "foo*" (toglob) to a pattern for future use */ 487 pat = evalstr(toglob, DOPAT|DOTILDE); 488 afree(toglob, ATEMP); 489 490 XPinit(w, 32); 491 492 glob_table(pat, &w, &keywords); 493 glob_table(pat, &w, &aliases); 494 glob_table(pat, &w, &builtins); 495 for (l = e->loc; l; l = l->next) 496 glob_table(pat, &w, &l->funs); 497 498 glob_path(flags, pat, &w, path); 499 if ((fpath = str_val(global("FPATH"))) != null) 500 glob_path(flags, pat, &w, fpath); 501 502 nwords = XPsize(w); 503 504 if (!nwords) { 505 *wordsp = (char **) 0; 506 XPfree(w); 507 return 0; 508 } 509 510 /* Sort entries */ 511 if (flags & XCF_FULLPATH) { 512 /* Sort by basename, then path order */ 513 struct path_order_info *info; 514 struct path_order_info *last_info = 0; 515 char **words = (char **) XPptrv(w); 516 int path_order = 0; 517 int i; 518 519 info = (struct path_order_info *) 520 alloc(sizeof(struct path_order_info) * nwords, ATEMP); 521 for (i = 0; i < nwords; i++) { 522 info[i].word = words[i]; 523 info[i].base = x_basename(words[i], (char *) 0); 524 if (!last_info || info[i].base != last_info->base || 525 strncmp(words[i], last_info->word, info[i].base) != 0) { 526 last_info = &info[i]; 527 path_order++; 528 } 529 info[i].path_order = path_order; 530 } 531 qsort(info, nwords, sizeof(struct path_order_info), 532 path_order_cmp); 533 for (i = 0; i < nwords; i++) 534 words[i] = info[i].word; 535 afree((void *) info, ATEMP); 536 } else { 537 /* Sort and remove duplicate entries */ 538 char **words = (char **) XPptrv(w); 539 int i, j; 540 541 qsortp(XPptrv(w), (size_t) nwords, xstrcmp); 542 543 for (i = j = 0; i < nwords - 1; i++) { 544 if (strcmp(words[i], words[i + 1])) 545 words[j++] = words[i]; 546 else 547 afree(words[i], ATEMP); 548 } 549 words[j++] = words[i]; 550 nwords = j; 551 w.cur = (void **) &words[j]; 552 } 553 554 XPput(w, NULL); 555 *wordsp = (char **) XPclose(w); 556 557 return nwords; 558 } 559 560 #define IS_WORDC(c) !( ctype(c, C_LEX1) || (c) == '\'' || (c) == '"' || \ 561 (c) == '`' || (c) == '=' || (c) == ':' ) 562 563 static int 564 x_locate_word(const char *buf, int buflen, int pos, int *startp, 565 int *is_commandp) 566 { 567 int p; 568 int start, end; 569 570 /* Bad call? Probably should report error */ 571 if (pos < 0 || pos > buflen) { 572 *startp = pos; 573 *is_commandp = 0; 574 return 0; 575 } 576 /* The case where pos == buflen happens to take care of itself... */ 577 578 start = pos; 579 /* Keep going backwards to start of word (has effect of allowing 580 * one blank after the end of a word) 581 */ 582 for (; (start > 0 && IS_WORDC(buf[start - 1])) || 583 (start > 1 && buf[start-2] == '\\'); start--) 584 ; 585 /* Go forwards to end of word */ 586 for (end = start; end < buflen && IS_WORDC(buf[end]); end++) { 587 if (buf[end] == '\\' && (end+1) < buflen) 588 end++; 589 } 590 591 if (is_commandp) { 592 int iscmd; 593 594 /* Figure out if this is a command */ 595 for (p = start - 1; p >= 0 && isspace(buf[p]); p--) 596 ; 597 iscmd = p < 0 || strchr(";|&()`", buf[p]); 598 if (iscmd) { 599 /* If command has a /, path, etc. is not searched; 600 * only current directory is searched, which is just 601 * like file globbing. 602 */ 603 for (p = start; p < end; p++) 604 if (buf[p] == '/') 605 break; 606 iscmd = p == end; 607 } 608 *is_commandp = iscmd; 609 } 610 611 *startp = start; 612 613 return end - start; 614 } 615 616 int 617 x_cf_glob(int flags, const char *buf, int buflen, int pos, int *startp, 618 int *endp, char ***wordsp, int *is_commandp) 619 { 620 int len; 621 int nwords; 622 char **words; 623 int is_command; 624 625 len = x_locate_word(buf, buflen, pos, startp, &is_command); 626 if (!(flags & XCF_COMMAND)) 627 is_command = 0; 628 /* Don't do command globing on zero length strings - it takes too 629 * long and isn't very useful. File globs are more likely to be 630 * useful, so allow these. 631 */ 632 if (len == 0 && is_command) 633 return 0; 634 635 nwords = (is_command ? x_command_glob : x_file_glob)(flags, 636 buf + *startp, len, &words); 637 if (nwords == 0) { 638 *wordsp = (char **) 0; 639 return 0; 640 } 641 642 if (is_commandp) 643 *is_commandp = is_command; 644 *wordsp = words; 645 *endp = *startp + len; 646 647 return nwords; 648 } 649 650 /* Given a string, copy it and possibly add a '*' to the end. The 651 * new string is returned. 652 */ 653 static char * 654 add_glob(const char *str, int slen) 655 { 656 char *toglob; 657 char *s; 658 bool saw_slash = false; 659 660 if (slen < 0) 661 return (char *) 0; 662 663 toglob = str_nsave(str, slen + 1, ATEMP); /* + 1 for "*" */ 664 toglob[slen] = '\0'; 665 666 /* 667 * If the pathname contains a wildcard (an unquoted '*', 668 * '?', or '[') or parameter expansion ('$'), or a ~username 669 * with no trailing slash, then it is globbed based on that 670 * value (i.e., without the appended '*'). 671 */ 672 for (s = toglob; *s; s++) { 673 if (*s == '\\' && s[1]) 674 s++; 675 else if (*s == '*' || *s == '[' || *s == '?' || *s == '$' 676 || (s[1] == '(' /*)*/ && strchr("*+?@!", *s))) 677 break; 678 else if (*s == '/') 679 saw_slash = true; 680 } 681 if (!*s && (*toglob != '~' || saw_slash)) { 682 toglob[slen] = '*'; 683 toglob[slen + 1] = '\0'; 684 } 685 686 return toglob; 687 } 688 689 /* 690 * Find longest common prefix 691 */ 692 int 693 x_longest_prefix(int nwords, char *const *words) 694 { 695 int i, j; 696 int prefix_len; 697 char *p; 698 699 if (nwords <= 0) 700 return 0; 701 702 prefix_len = strlen(words[0]); 703 for (i = 1; i < nwords; i++) 704 for (j = 0, p = words[i]; j < prefix_len; j++) 705 if (p[j] != words[0][j]) { 706 prefix_len = j; 707 break; 708 } 709 return prefix_len; 710 } 711 712 void 713 x_free_words(int nwords, char **words) 714 { 715 int i; 716 717 for (i = 0; i < nwords; i++) 718 if (words[i]) 719 afree(words[i], ATEMP); 720 afree(words, ATEMP); 721 } 722 723 /* Return the offset of the basename of string s (which ends at se - need not 724 * be null terminated). Trailing slashes are ignored. If s is just a slash, 725 * then the offset is 0 (actually, length - 1). 726 * s Return 727 * /etc 1 728 * /etc/ 1 729 * /etc// 1 730 * /etc/fo 5 731 * foo 0 732 * /// 2 733 * 0 734 */ 735 int 736 x_basename(const char *s, const char *se) 737 { 738 const char *p; 739 740 if (se == (char *) 0) 741 se = s + strlen(s); 742 if (s == se) 743 return 0; 744 745 /* Skip trailing slashes */ 746 for (p = se - 1; p > s && *p == '/'; p--) 747 ; 748 for (; p > s && *p != '/'; p--) 749 ; 750 if (*p == '/' && p + 1 < se) 751 p++; 752 753 return p - s; 754 } 755 756 /* 757 * Apply pattern matching to a table: all table entries that match a pattern 758 * are added to wp. 759 */ 760 static void 761 glob_table(const char *pat, XPtrV *wp, struct table *tp) 762 { 763 struct tstate ts; 764 struct tbl *te; 765 766 for (ktwalk(&ts, tp); (te = ktnext(&ts)); ) { 767 if (gmatch(te->name, pat, false)) 768 XPput(*wp, str_save(te->name, ATEMP)); 769 } 770 } 771 772 static void 773 glob_path(int flags, const char *pat, XPtrV *wp, const char *path) 774 { 775 const char *sp, *p; 776 char *xp; 777 int staterr; 778 int pathlen; 779 int patlen; 780 int oldsize, newsize, i, j; 781 char **words; 782 XString xs; 783 784 patlen = strlen(pat) + 1; 785 sp = path; 786 Xinit(xs, xp, patlen + 128, ATEMP); 787 while (sp) { 788 xp = Xstring(xs, xp); 789 if (!(p = strchr(sp, ':'))) 790 p = sp + strlen(sp); 791 pathlen = p - sp; 792 if (pathlen) { 793 /* Copy sp into xp, stuffing any MAGIC characters 794 * on the way 795 */ 796 const char *s = sp; 797 798 XcheckN(xs, xp, pathlen * 2); 799 while (s < p) { 800 if (ISMAGIC(*s)) 801 *xp++ = MAGIC; 802 *xp++ = *s++; 803 } 804 *xp++ = '/'; 805 pathlen++; 806 } 807 sp = p; 808 XcheckN(xs, xp, patlen); 809 memcpy(xp, pat, patlen); 810 811 oldsize = XPsize(*wp); 812 glob_str(Xstring(xs, xp), wp, 1); /* mark dirs */ 813 newsize = XPsize(*wp); 814 815 /* Check that each match is executable... */ 816 words = (char **) XPptrv(*wp); 817 for (i = j = oldsize; i < newsize; i++) { 818 staterr = 0; 819 if ((search_access(words[i], X_OK, &staterr) >= 0) || 820 (staterr == EISDIR)) { 821 words[j] = words[i]; 822 if (!(flags & XCF_FULLPATH)) 823 memmove(words[j], words[j] + pathlen, 824 strlen(words[j] + pathlen) + 1); 825 j++; 826 } else 827 afree(words[i], ATEMP); 828 } 829 wp->cur = (void **) &words[j]; 830 831 if (!*sp++) 832 break; 833 } 834 Xfree(xs, xp); 835 } 836 837 /* 838 * if argument string contains any special characters, they will 839 * be escaped and the result will be put into edit buffer by 840 * keybinding-specific function 841 */ 842 int 843 x_escape(const char *s, size_t len, int (*putbuf_func) (const char *, size_t)) 844 { 845 size_t add, wlen; 846 const char *ifs = str_val(local("IFS", 0)); 847 int rval=0; 848 849 for (add = 0, wlen = len; wlen - add > 0; add++) { 850 if (strchr("\\$(){}[]?*&;#|<>\"'`", s[add]) || strchr(ifs, s[add])) { 851 if (putbuf_func(s, add) != 0) { 852 rval = -1; 853 break; 854 } 855 856 putbuf_func("\\", 1); 857 putbuf_func(&s[add], 1); 858 859 add++; 860 wlen -= add; 861 s += add; 862 add = -1; /* after the increment it will go to 0 */ 863 } 864 } 865 if (wlen > 0 && rval == 0) 866 rval = putbuf_func(s, wlen); 867 868 return (rval); 869 } 870 #endif /* EDIT */ 871