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