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