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