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