1 /*- 2 * Copyright (c) 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; 36 #endif 37 #endif /* not lint */ 38 #include <sys/cdefs.h> 39 #include <sys/param.h> 40 #include <sys/stat.h> 41 #include <dirent.h> 42 #include <errno.h> 43 #include <fcntl.h> 44 #include <limits.h> 45 #include <paths.h> 46 #include <stdbool.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <unistd.h> 50 /* 51 * Editline and history functions (and glue). 52 */ 53 #include "alias.h" 54 #include "shell.h" 55 #include "parser.h" 56 #include "var.h" 57 #include "options.h" 58 #include "main.h" 59 #include "output.h" 60 #include "mystring.h" 61 #include "builtins.h" 62 #ifndef NO_HISTORY 63 #include "myhistedit.h" 64 #include "error.h" 65 #include "eval.h" 66 #include "memalloc.h" 67 68 #define MAXHISTLOOPS 4 /* max recursions through fc */ 69 #define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ 70 71 History *hist; /* history cookie */ 72 EditLine *el; /* editline cookie */ 73 int displayhist; 74 static int savehist; 75 static FILE *el_in, *el_out; 76 static bool in_command_completion; 77 78 static char *fc_replace(const char *, char *, char *); 79 static int not_fcnumber(const char *); 80 static int str_to_event(const char *, int); 81 static int comparator(const void *, const void *, void *); 82 static char **sh_matches(const char *, int, int); 83 static const char *append_char_function(const char *); 84 static unsigned char sh_complete(EditLine *, int); 85 86 static const char * 87 get_histfile(void) 88 { 89 const char *histfile; 90 91 /* don't try to save if the history size is 0 */ 92 if (hist == NULL || !strcmp(histsizeval(), "0")) 93 return (NULL); 94 histfile = expandstr("${HISTFILE-${HOME-}/.sh_history}"); 95 96 if (histfile[0] == '\0') 97 return (NULL); 98 return (histfile); 99 } 100 101 void 102 histsave(void) 103 { 104 HistEvent he; 105 char *histtmpname = NULL; 106 const char *histfile; 107 int fd; 108 FILE *f; 109 110 if (!savehist || (histfile = get_histfile()) == NULL) 111 return; 112 INTOFF; 113 asprintf(&histtmpname, "%s.XXXXXXXXXX", histfile); 114 if (histtmpname == NULL) { 115 INTON; 116 return; 117 } 118 fd = mkstemp(histtmpname); 119 if (fd == -1 || (f = fdopen(fd, "w")) == NULL) { 120 free(histtmpname); 121 INTON; 122 return; 123 } 124 if (history(hist, &he, H_SAVE_FP, f) < 1 || 125 rename(histtmpname, histfile) == -1) 126 unlink(histtmpname); 127 fclose(f); 128 free(histtmpname); 129 INTON; 130 131 } 132 133 void 134 histload(void) 135 { 136 const char *histfile; 137 HistEvent he; 138 139 if ((histfile = get_histfile()) == NULL) 140 return; 141 errno = 0; 142 if (history(hist, &he, H_LOAD, histfile) != -1 || errno == ENOENT) 143 savehist = 1; 144 } 145 146 /* 147 * Set history and editing status. Called whenever the status may 148 * have changed (figures out what to do). 149 */ 150 void 151 histedit(void) 152 { 153 154 #define editing (Eflag || Vflag) 155 156 if (iflag) { 157 if (!hist) { 158 /* 159 * turn history on 160 */ 161 INTOFF; 162 hist = history_init(); 163 INTON; 164 165 if (hist != NULL) 166 sethistsize(histsizeval()); 167 else 168 out2fmt_flush("sh: can't initialize history\n"); 169 } 170 if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ 171 /* 172 * turn editing on 173 */ 174 char *term; 175 176 INTOFF; 177 if (el_in == NULL) 178 el_in = fdopen(0, "r"); 179 if (el_out == NULL) 180 el_out = fdopen(2, "w"); 181 if (el_in == NULL || el_out == NULL) 182 goto bad; 183 term = lookupvar("TERM"); 184 if (term) 185 setenv("TERM", term, 1); 186 else 187 unsetenv("TERM"); 188 el = el_init(arg0, el_in, el_out, el_out); 189 if (el != NULL) { 190 if (hist) 191 el_set(el, EL_HIST, history, hist); 192 el_set(el, EL_PROMPT_ESC, getprompt, '\001'); 193 el_set(el, EL_ADDFN, "sh-complete", 194 "Filename completion", 195 sh_complete); 196 } else { 197 bad: 198 out2fmt_flush("sh: can't initialize editing\n"); 199 } 200 INTON; 201 } else if (!editing && el) { 202 INTOFF; 203 el_end(el); 204 el = NULL; 205 INTON; 206 } 207 if (el) { 208 if (Vflag) 209 el_set(el, EL_EDITOR, "vi"); 210 else if (Eflag) { 211 el_set(el, EL_EDITOR, "emacs"); 212 } 213 el_set(el, EL_BIND, "^I", "sh-complete", NULL); 214 el_source(el, NULL); 215 } 216 } else { 217 INTOFF; 218 if (el) { /* no editing if not interactive */ 219 el_end(el); 220 el = NULL; 221 } 222 if (hist) { 223 history_end(hist); 224 hist = NULL; 225 } 226 INTON; 227 } 228 } 229 230 231 void 232 sethistsize(const char *hs) 233 { 234 int histsize; 235 HistEvent he; 236 237 if (hist != NULL) { 238 if (hs == NULL || !is_number(hs)) 239 histsize = 100; 240 else 241 histsize = atoi(hs); 242 history(hist, &he, H_SETSIZE, histsize); 243 history(hist, &he, H_SETUNIQUE, 1); 244 } 245 } 246 247 void 248 setterm(const char *term) 249 { 250 if (rootshell && el != NULL && term != NULL) 251 el_set(el, EL_TERMINAL, term); 252 } 253 254 int 255 histcmd(int argc, char **argv __unused) 256 { 257 const char *editor = NULL; 258 HistEvent he; 259 int lflg = 0, nflg = 0, rflg = 0, sflg = 0; 260 int i, retval; 261 const char *firststr, *laststr; 262 int first, last, direction; 263 char *pat = NULL, *repl = NULL; 264 static int active = 0; 265 struct jmploc jmploc; 266 struct jmploc *savehandler; 267 char editfilestr[PATH_MAX]; 268 char *volatile editfile; 269 FILE *efp = NULL; 270 int oldhistnum; 271 272 if (hist == NULL) 273 error("history not active"); 274 275 if (argc == 1) 276 error("missing history argument"); 277 278 while (not_fcnumber(*argptr)) 279 do { 280 switch (nextopt("e:lnrs")) { 281 case 'e': 282 editor = shoptarg; 283 break; 284 case 'l': 285 lflg = 1; 286 break; 287 case 'n': 288 nflg = 1; 289 break; 290 case 'r': 291 rflg = 1; 292 break; 293 case 's': 294 sflg = 1; 295 break; 296 case '\0': 297 goto operands; 298 } 299 } while (nextopt_optptr != NULL); 300 operands: 301 savehandler = handler; 302 /* 303 * If executing... 304 */ 305 if (lflg == 0 || editor || sflg) { 306 lflg = 0; /* ignore */ 307 editfile = NULL; 308 /* 309 * Catch interrupts to reset active counter and 310 * cleanup temp files. 311 */ 312 if (setjmp(jmploc.loc)) { 313 active = 0; 314 if (editfile) 315 unlink(editfile); 316 handler = savehandler; 317 longjmp(handler->loc, 1); 318 } 319 handler = &jmploc; 320 if (++active > MAXHISTLOOPS) { 321 active = 0; 322 displayhist = 0; 323 error("called recursively too many times"); 324 } 325 /* 326 * Set editor. 327 */ 328 if (sflg == 0) { 329 if (editor == NULL && 330 (editor = bltinlookup("FCEDIT", 1)) == NULL && 331 (editor = bltinlookup("EDITOR", 1)) == NULL) 332 editor = DEFEDITOR; 333 if (editor[0] == '-' && editor[1] == '\0') { 334 sflg = 1; /* no edit */ 335 editor = NULL; 336 } 337 } 338 } 339 340 /* 341 * If executing, parse [old=new] now 342 */ 343 if (lflg == 0 && *argptr != NULL && 344 ((repl = strchr(*argptr, '=')) != NULL)) { 345 pat = *argptr; 346 *repl++ = '\0'; 347 argptr++; 348 } 349 /* 350 * determine [first] and [last] 351 */ 352 if (*argptr == NULL) { 353 firststr = lflg ? "-16" : "-1"; 354 laststr = "-1"; 355 } else if (argptr[1] == NULL) { 356 firststr = argptr[0]; 357 laststr = lflg ? "-1" : argptr[0]; 358 } else if (argptr[2] == NULL) { 359 firststr = argptr[0]; 360 laststr = argptr[1]; 361 } else 362 error("too many arguments"); 363 /* 364 * Turn into event numbers. 365 */ 366 first = str_to_event(firststr, 0); 367 last = str_to_event(laststr, 1); 368 369 if (rflg) { 370 i = last; 371 last = first; 372 first = i; 373 } 374 /* 375 * XXX - this should not depend on the event numbers 376 * always increasing. Add sequence numbers or offset 377 * to the history element in next (diskbased) release. 378 */ 379 direction = first < last ? H_PREV : H_NEXT; 380 381 /* 382 * If editing, grab a temp file. 383 */ 384 if (editor) { 385 int fd; 386 INTOFF; /* easier */ 387 sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP); 388 if ((fd = mkstemp(editfilestr)) < 0) 389 error("can't create temporary file %s", editfile); 390 editfile = editfilestr; 391 if ((efp = fdopen(fd, "w")) == NULL) { 392 close(fd); 393 error("Out of space"); 394 } 395 } 396 397 /* 398 * Loop through selected history events. If listing or executing, 399 * do it now. Otherwise, put into temp file and call the editor 400 * after. 401 * 402 * The history interface needs rethinking, as the following 403 * convolutions will demonstrate. 404 */ 405 history(hist, &he, H_FIRST); 406 retval = history(hist, &he, H_NEXT_EVENT, first); 407 for (;retval != -1; retval = history(hist, &he, direction)) { 408 if (lflg) { 409 if (!nflg) 410 out1fmt("%5d ", he.num); 411 out1str(he.str); 412 } else { 413 const char *s = pat ? 414 fc_replace(he.str, pat, repl) : he.str; 415 416 if (sflg) { 417 if (displayhist) { 418 out2str(s); 419 flushout(out2); 420 } 421 evalstring(s, 0); 422 if (displayhist && hist) { 423 /* 424 * XXX what about recursive and 425 * relative histnums. 426 */ 427 oldhistnum = he.num; 428 history(hist, &he, H_ENTER, s); 429 /* 430 * XXX H_ENTER moves the internal 431 * cursor, set it back to the current 432 * entry. 433 */ 434 history(hist, &he, 435 H_NEXT_EVENT, oldhistnum); 436 } 437 } else 438 fputs(s, efp); 439 } 440 /* 441 * At end? (if we were to lose last, we'd sure be 442 * messed up). 443 */ 444 if (he.num == last) 445 break; 446 } 447 if (editor) { 448 char *editcmd; 449 450 fclose(efp); 451 INTON; 452 editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); 453 sprintf(editcmd, "%s %s", editor, editfile); 454 evalstring(editcmd, 0); /* XXX - should use no JC command */ 455 readcmdfile(editfile, 0 /* verify */); /* XXX - should read back - quick tst */ 456 unlink(editfile); 457 } 458 459 if (lflg == 0 && active > 0) 460 --active; 461 if (displayhist) 462 displayhist = 0; 463 handler = savehandler; 464 return 0; 465 } 466 467 static char * 468 fc_replace(const char *s, char *p, char *r) 469 { 470 char *dest; 471 int plen = strlen(p); 472 473 STARTSTACKSTR(dest); 474 while (*s) { 475 if (*s == *p && strncmp(s, p, plen) == 0) { 476 STPUTS(r, dest); 477 s += plen; 478 *p = '\0'; /* so no more matches */ 479 } else 480 STPUTC(*s++, dest); 481 } 482 STPUTC('\0', dest); 483 dest = grabstackstr(dest); 484 485 return (dest); 486 } 487 488 static int 489 not_fcnumber(const char *s) 490 { 491 if (s == NULL) 492 return (0); 493 if (*s == '-') 494 s++; 495 return (!is_number(s)); 496 } 497 498 static int 499 str_to_event(const char *str, int last) 500 { 501 HistEvent he; 502 const char *s = str; 503 int relative = 0; 504 int i, retval; 505 506 retval = history(hist, &he, H_FIRST); 507 switch (*s) { 508 case '-': 509 relative = 1; 510 /*FALLTHROUGH*/ 511 case '+': 512 s++; 513 } 514 if (is_number(s)) { 515 i = atoi(s); 516 if (relative) { 517 while (retval != -1 && i--) { 518 retval = history(hist, &he, H_NEXT); 519 } 520 if (retval == -1) 521 retval = history(hist, &he, H_LAST); 522 } else { 523 retval = history(hist, &he, H_NEXT_EVENT, i); 524 if (retval == -1) { 525 /* 526 * the notion of first and last is 527 * backwards to that of the history package 528 */ 529 retval = history(hist, &he, last ? H_FIRST : H_LAST); 530 } 531 } 532 if (retval == -1) 533 error("history number %s not found (internal error)", 534 str); 535 } else { 536 /* 537 * pattern 538 */ 539 retval = history(hist, &he, H_PREV_STR, str); 540 if (retval == -1) 541 error("history pattern not found: %s", str); 542 } 543 return (he.num); 544 } 545 546 int 547 bindcmd(int argc, char **argv) 548 { 549 int ret; 550 FILE *old; 551 FILE *out; 552 553 if (el == NULL) 554 error("line editing is disabled"); 555 556 INTOFF; 557 558 out = out1fp(); 559 if (out == NULL) 560 error("Out of space"); 561 562 el_get(el, EL_GETFP, 1, &old); 563 el_set(el, EL_SETFP, 1, out); 564 565 ret = el_parse(el, argc, __DECONST(const char **, argv)); 566 567 el_set(el, EL_SETFP, 1, old); 568 569 fclose(out); 570 571 if (argc > 1 && argv[1][0] == '-' && 572 memchr("ve", argv[1][1], 2) != NULL) { 573 Vflag = argv[1][1] == 'v'; 574 Eflag = !Vflag; 575 histedit(); 576 } 577 578 INTON; 579 580 return ret; 581 } 582 583 /* 584 * Comparator function for qsort(). The use of curpos here is to skip 585 * characters that we already know to compare equal (common prefix). 586 */ 587 static int 588 comparator(const void *a, const void *b, void *thunk) 589 { 590 size_t curpos = (intptr_t)thunk; 591 592 return (strcmp(*(char *const *)a + curpos, 593 *(char *const *)b + curpos)); 594 } 595 596 static char 597 **add_match(char **matches, size_t i, size_t *size, char *match_copy) 598 { 599 if (match_copy == NULL) 600 return (NULL); 601 matches[i] = match_copy; 602 if (i >= *size - 1) { 603 *size *= 2; 604 matches = reallocarray(matches, *size, sizeof(matches[0])); 605 } 606 607 return (matches); 608 } 609 610 /* 611 * This function is passed to libedit's fn_complete2(). The library will use 612 * it instead of its standard function that finds matching files in current 613 * directory. If we're at the start of the line, we want to look for 614 * available commands from all paths in $PATH. 615 */ 616 static char 617 **sh_matches(const char *text, int start, int end) 618 { 619 char *free_path = NULL, *path; 620 const char *dirname; 621 char **matches = NULL, **rmatches; 622 size_t i = 0, size = 16, uniq; 623 size_t curpos = end - start, lcstring = -1; 624 625 in_command_completion = false; 626 if (start > 0 || memchr("/.~", text[0], 3) != NULL) 627 return (NULL); 628 in_command_completion = true; 629 if ((free_path = path = strdup(pathval())) == NULL) 630 goto out; 631 if ((matches = malloc(size * sizeof(matches[0]))) == NULL) 632 goto out; 633 while ((dirname = strsep(&path, ":")) != NULL) { 634 struct dirent *entry; 635 DIR *dir; 636 int dfd; 637 638 dir = opendir(dirname[0] == '\0' ? "." : dirname); 639 if (dir == NULL) 640 continue; 641 if ((dfd = dirfd(dir)) == -1) { 642 closedir(dir); 643 continue; 644 } 645 while ((entry = readdir(dir)) != NULL) { 646 struct stat statb; 647 648 if (strncmp(entry->d_name, text, curpos) != 0) 649 continue; 650 if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) { 651 if (fstatat(dfd, entry->d_name, &statb, 0) == -1) 652 continue; 653 if (!S_ISREG(statb.st_mode)) 654 continue; 655 } else if (entry->d_type != DT_REG) 656 continue; 657 rmatches = add_match(matches, ++i, &size, 658 strdup(entry->d_name)); 659 if (rmatches == NULL) { 660 closedir(dir); 661 goto out; 662 } 663 matches = rmatches; 664 } 665 closedir(dir); 666 } 667 for (const unsigned char *bp = builtincmd; *bp != 0; bp += 2 + bp[0]) { 668 if (curpos > bp[0] || memcmp(bp + 2, text, curpos) != 0) 669 continue; 670 rmatches = add_match(matches, ++i, &size, strndup(bp + 2, bp[0])); 671 if (rmatches == NULL) 672 goto out; 673 matches = rmatches; 674 } 675 for (const struct alias *ap = NULL; (ap = iteralias(ap)) != NULL;) { 676 if (strncmp(ap->name, text, curpos) != 0) 677 continue; 678 rmatches = add_match(matches, ++i, &size, strdup(ap->name)); 679 if (rmatches == NULL) 680 goto out; 681 matches = rmatches; 682 } 683 out: 684 free(free_path); 685 if (i == 0) { 686 free(matches); 687 return (NULL); 688 } 689 uniq = 1; 690 if (i > 1) { 691 qsort_s(matches + 1, i, sizeof(matches[0]), comparator, 692 (void *)(intptr_t)curpos); 693 for (size_t k = 2; k <= i; k++) { 694 const char *l = matches[uniq] + curpos; 695 const char *r = matches[k] + curpos; 696 size_t common = 0; 697 698 while (*l != '\0' && *r != '\0' && *l == *r) 699 (void)l++, r++, common++; 700 if (common < lcstring) 701 lcstring = common; 702 if (*l == *r) 703 free(matches[k]); 704 else 705 matches[++uniq] = matches[k]; 706 } 707 } 708 matches[uniq + 1] = NULL; 709 /* 710 * matches[0] is special: it's not a real matching file name but 711 * a common prefix for all matching names. It can't be null, unlike 712 * any other element of the array. When strings matches[0] and 713 * matches[1] compare equal and matches[2] is null that means to 714 * libedit that there is only a single match. It will then replace 715 * user input with possibly escaped string in matches[0] which is the 716 * reason to copy the full name of the only match. 717 */ 718 if (uniq == 1) 719 matches[0] = strdup(matches[1]); 720 else if (lcstring != (size_t)-1) 721 matches[0] = strndup(matches[1], curpos + lcstring); 722 else 723 matches[0] = strdup(text); 724 if (matches[0] == NULL) { 725 for (size_t k = 1; k <= uniq; k++) 726 free(matches[k]); 727 free(matches); 728 return (NULL); 729 } 730 return (matches); 731 } 732 733 /* 734 * If we don't specify this function as app_func in the call to fn_complete2, 735 * libedit will use the default one, which adds a " " to plain files and 736 * a "/" to directories regardless of whether it's a command name or a plain 737 * path (relative or absolute). We never want to add "/" to commands. 738 * 739 * For example, after I did "mkdir rmdir", "rmdi" would be autocompleted to 740 * "rmdir/" instead of "rmdir ". 741 */ 742 static const char * 743 append_char_function(const char *name) 744 { 745 struct stat stbuf; 746 char *expname = name[0] == '~' ? fn_tilde_expand(name) : NULL; 747 const char *rs; 748 749 if (!in_command_completion && 750 stat(expname ? expname : name, &stbuf) == 0 && 751 S_ISDIR(stbuf.st_mode)) 752 rs = "/"; 753 else 754 rs = " "; 755 free(expname); 756 return (rs); 757 } 758 759 /* 760 * This is passed to el_set(el, EL_ADDFN, ...) so that it's possible to 761 * bind a key (tab by default) to execute the function. 762 */ 763 unsigned char 764 sh_complete(EditLine *sel, int ch __unused) 765 { 766 return (unsigned char)fn_complete2(sel, NULL, sh_matches, 767 L" \t\n\"\\'`@$><=;|&{(", NULL, append_char_function, 768 (size_t)100, NULL, &((int) {0}), NULL, NULL, FN_QUOTE_MATCH); 769 } 770 771 #else 772 #include "error.h" 773 774 int 775 histcmd(int argc __unused, char **argv __unused) 776 { 777 778 error("not compiled with history support"); 779 /*NOTREACHED*/ 780 return (0); 781 } 782 783 int 784 bindcmd(int argc __unused, char **argv __unused) 785 { 786 787 error("not compiled with line editing support"); 788 return (0); 789 } 790 #endif 791