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