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