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