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