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