1 /* $OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $ */ 2 /* $OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $ */ 3 4 /*- 5 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 6 * Thorsten Glaser <tg (at) mirbsd.org> 7 * 8 * Provided that these terms and disclaimer and all copyright notices 9 * are retained or reproduced in an accompanying document, permission 10 * is granted to deal in this work without restriction, including un- 11 * limited rights to use, publicly perform, distribute, sell, modify, 12 * merge, give away, or sublicence. 13 * 14 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to 15 * the utmost extent permitted by applicable law, neither express nor 16 * implied; without malicious intent or gross negligence. In no event 17 * may a licensor, author or contributor be held liable for indirect, 18 * direct, other damage, loss, or other issues arising in any way out 19 * of dealing in the work, even if advised of the possibility of such 20 * damage or existence of a defect, except proven that it results out 21 * of said person's immediate fault when using the work as intended. 22 */ 23 24 #include "sh.h" 25 #if HAVE_PERSISTENT_HISTORY 26 #include <sys/file.h> 27 #endif 28 29 __RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.98 2010/07/24 17:08:29 tg Exp $"); 30 31 /*- 32 * MirOS: This is the default mapping type, and need not be specified. 33 * IRIX doesn't have this constant. 34 */ 35 #ifndef MAP_FILE 36 #define MAP_FILE 0 37 #endif 38 39 Trap sigtraps[NSIG + 1]; 40 static struct sigaction Sigact_ign; 41 42 #if HAVE_PERSISTENT_HISTORY 43 static int hist_count_lines(unsigned char *, int); 44 static int hist_shrink(unsigned char *, int); 45 static unsigned char *hist_skip_back(unsigned char *,int *,int); 46 static void histload(Source *, unsigned char *, int); 47 static void histinsert(Source *, int, const char *); 48 static void writehistfile(int, char *); 49 static int sprinkle(int); 50 #endif 51 52 static int hist_execute(char *); 53 static int hist_replace(char **, const char *, const char *, bool); 54 static char **hist_get(const char *, bool, bool); 55 static char **hist_get_oldest(void); 56 static void histbackup(void); 57 58 static char **current; /* current position in history[] */ 59 static int hstarted; /* set after hist_init() called */ 60 static Source *hist_source; 61 62 #if HAVE_PERSISTENT_HISTORY 63 static char *hname; /* current name of history file */ 64 static int histfd; 65 static int hsize; 66 #endif 67 68 int 69 c_fc(const char **wp) 70 { 71 struct shf *shf; 72 struct temp *tf; 73 const char *p; 74 char *editor = NULL; 75 bool gflag = false, lflag = false, nflag = false, rflag = false, 76 sflag = false; 77 int optc; 78 const char *first = NULL, *last = NULL; 79 char **hfirst, **hlast, **hp; 80 81 if (!Flag(FTALKING_I)) { 82 bi_errorf("history functions not available"); 83 return (1); 84 } 85 86 while ((optc = ksh_getopt(wp, &builtin_opt, 87 "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1) 88 switch (optc) { 89 case 'e': 90 p = builtin_opt.optarg; 91 if (ksh_isdash(p)) 92 sflag = true; 93 else { 94 size_t len = strlen(p); 95 editor = alloc(len + 4, ATEMP); 96 memcpy(editor, p, len); 97 memcpy(editor + len, " $_", 4); 98 } 99 break; 100 case 'g': /* non-AT&T ksh */ 101 gflag = true; 102 break; 103 case 'l': 104 lflag = true; 105 break; 106 case 'n': 107 nflag = true; 108 break; 109 case 'r': 110 rflag = true; 111 break; 112 case 's': /* POSIX version of -e - */ 113 sflag = true; 114 break; 115 /* kludge city - accept -num as -- -num (kind of) */ 116 case '0': case '1': case '2': case '3': case '4': 117 case '5': case '6': case '7': case '8': case '9': 118 p = shf_smprintf("-%c%s", 119 optc, builtin_opt.optarg); 120 if (!first) 121 first = p; 122 else if (!last) 123 last = p; 124 else { 125 bi_errorf("too many arguments"); 126 return (1); 127 } 128 break; 129 case '?': 130 return (1); 131 } 132 wp += builtin_opt.optind; 133 134 /* Substitute and execute command */ 135 if (sflag) { 136 char *pat = NULL, *rep = NULL; 137 138 if (editor || lflag || nflag || rflag) { 139 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)"); 140 return (1); 141 } 142 143 /* Check for pattern replacement argument */ 144 if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) { 145 strdupx(pat, *wp, ATEMP); 146 rep = pat + (p - *wp); 147 *rep++ = '\0'; 148 wp++; 149 } 150 /* Check for search prefix */ 151 if (!first && (first = *wp)) 152 wp++; 153 if (last || *wp) { 154 bi_errorf("too many arguments"); 155 return (1); 156 } 157 158 hp = first ? hist_get(first, false, false) : 159 hist_get_newest(false); 160 if (!hp) 161 return (1); 162 return (hist_replace(hp, pat, rep, gflag)); 163 } 164 165 if (editor && (lflag || nflag)) { 166 bi_errorf("can't use -l, -n with -e"); 167 return (1); 168 } 169 170 if (!first && (first = *wp)) 171 wp++; 172 if (!last && (last = *wp)) 173 wp++; 174 if (*wp) { 175 bi_errorf("too many arguments"); 176 return (1); 177 } 178 if (!first) { 179 hfirst = lflag ? hist_get("-16", true, true) : 180 hist_get_newest(false); 181 if (!hfirst) 182 return (1); 183 /* can't fail if hfirst didn't fail */ 184 hlast = hist_get_newest(false); 185 } else { 186 /* POSIX says not an error if first/last out of bounds 187 * when range is specified; AT&T ksh and pdksh allow out of 188 * bounds for -l as well. 189 */ 190 hfirst = hist_get(first, (lflag || last) ? true : false, lflag); 191 if (!hfirst) 192 return (1); 193 hlast = last ? hist_get(last, true, lflag) : 194 (lflag ? hist_get_newest(false) : hfirst); 195 if (!hlast) 196 return (1); 197 } 198 if (hfirst > hlast) { 199 char **temp; 200 201 temp = hfirst; hfirst = hlast; hlast = temp; 202 rflag = !rflag; /* POSIX */ 203 } 204 205 /* List history */ 206 if (lflag) { 207 char *s, *t; 208 209 for (hp = rflag ? hlast : hfirst; 210 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) { 211 if (!nflag) 212 shf_fprintf(shl_stdout, "%d", 213 hist_source->line - (int)(histptr - hp)); 214 shf_putc('\t', shl_stdout); 215 /* print multi-line commands correctly */ 216 s = *hp; 217 while ((t = strchr(s, '\n'))) { 218 *t = '\0'; 219 shf_fprintf(shl_stdout, "%s\n\t", s); 220 *t++ = '\n'; 221 s = t; 222 } 223 shf_fprintf(shl_stdout, "%s\n", s); 224 } 225 shf_flush(shl_stdout); 226 return (0); 227 } 228 229 /* Run editor on selected lines, then run resulting commands */ 230 231 tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps); 232 if (!(shf = tf->shf)) { 233 bi_errorf("cannot create temp file %s - %s", 234 tf->name, strerror(errno)); 235 return (1); 236 } 237 for (hp = rflag ? hlast : hfirst; 238 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) 239 shf_fprintf(shf, "%s\n", *hp); 240 if (shf_close(shf) == EOF) { 241 bi_errorf("error writing temporary file - %s", strerror(errno)); 242 return (1); 243 } 244 245 /* Ignore setstr errors here (arbitrary) */ 246 setstr(local("_", false), tf->name, KSH_RETURN_ERROR); 247 248 /* XXX: source should not get trashed by this.. */ 249 { 250 Source *sold = source; 251 int ret; 252 253 ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_", 0); 254 source = sold; 255 if (ret) 256 return (ret); 257 } 258 259 { 260 struct stat statb; 261 XString xs; 262 char *xp; 263 int n; 264 265 if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) { 266 bi_errorf("cannot open temp file %s", tf->name); 267 return (1); 268 } 269 270 n = stat(tf->name, &statb) < 0 ? 128 : statb.st_size + 1; 271 Xinit(xs, xp, n, hist_source->areap); 272 while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) { 273 xp += n; 274 if (Xnleft(xs, xp) <= 0) 275 XcheckN(xs, xp, Xlength(xs, xp)); 276 } 277 if (n < 0) { 278 bi_errorf("error reading temp file %s - %s", 279 tf->name, strerror(shf_errno(shf))); 280 shf_close(shf); 281 return (1); 282 } 283 shf_close(shf); 284 *xp = '\0'; 285 strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); 286 return (hist_execute(Xstring(xs, xp))); 287 } 288 } 289 290 /* Save cmd in history, execute cmd (cmd gets trashed) */ 291 static int 292 hist_execute(char *cmd) 293 { 294 Source *sold; 295 int ret; 296 char *p, *q; 297 298 histbackup(); 299 300 for (p = cmd; p; p = q) { 301 if ((q = strchr(p, '\n'))) { 302 *q++ = '\0'; /* kill the newline */ 303 if (!*q) /* ignore trailing newline */ 304 q = NULL; 305 } 306 histsave(&hist_source->line, p, true, true); 307 308 shellf("%s\n", p); /* POSIX doesn't say this is done... */ 309 if (q) /* restore \n (trailing \n not restored) */ 310 q[-1] = '\n'; 311 } 312 313 /* 314 * Commands are executed here instead of pushing them onto the 315 * input 'cause POSIX says the redirection and variable assignments 316 * in 317 * X=y fc -e - 42 2> /dev/null 318 * are to effect the repeated commands environment. 319 */ 320 /* XXX: source should not get trashed by this.. */ 321 sold = source; 322 ret = command(cmd, 0); 323 source = sold; 324 return (ret); 325 } 326 327 static int 328 hist_replace(char **hp, const char *pat, const char *rep, bool globr) 329 { 330 char *line; 331 332 if (!pat) 333 strdupx(line, *hp, ATEMP); 334 else { 335 char *s, *s1; 336 int pat_len = strlen(pat); 337 int rep_len = strlen(rep); 338 int len; 339 XString xs; 340 char *xp; 341 bool any_subst = false; 342 343 Xinit(xs, xp, 128, ATEMP); 344 for (s = *hp; (s1 = strstr(s, pat)) && (!any_subst || globr); 345 s = s1 + pat_len) { 346 any_subst = true; 347 len = s1 - s; 348 XcheckN(xs, xp, len + rep_len); 349 memcpy(xp, s, len); /* first part */ 350 xp += len; 351 memcpy(xp, rep, rep_len); /* replacement */ 352 xp += rep_len; 353 } 354 if (!any_subst) { 355 bi_errorf("substitution failed"); 356 return (1); 357 } 358 len = strlen(s) + 1; 359 XcheckN(xs, xp, len); 360 memcpy(xp, s, len); 361 xp += len; 362 line = Xclose(xs, xp); 363 } 364 return (hist_execute(line)); 365 } 366 367 /* 368 * get pointer to history given pattern 369 * pattern is a number or string 370 */ 371 static char ** 372 hist_get(const char *str, bool approx, bool allow_cur) 373 { 374 char **hp = NULL; 375 int n; 376 377 if (getn(str, &n)) { 378 hp = histptr + (n < 0 ? n : (n - hist_source->line)); 379 if ((ptrdiff_t)hp < (ptrdiff_t)history) { 380 if (approx) 381 hp = hist_get_oldest(); 382 else { 383 bi_errorf("%s: not in history", str); 384 hp = NULL; 385 } 386 } else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) { 387 if (approx) 388 hp = hist_get_newest(allow_cur); 389 else { 390 bi_errorf("%s: not in history", str); 391 hp = NULL; 392 } 393 } else if (!allow_cur && hp == histptr) { 394 bi_errorf("%s: invalid range", str); 395 hp = NULL; 396 } 397 } else { 398 int anchored = *str == '?' ? (++str, 0) : 1; 399 400 /* the -1 is to avoid the current fc command */ 401 if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0) 402 bi_errorf("%s: not in history", str); 403 else 404 hp = &history[n]; 405 } 406 return (hp); 407 } 408 409 /* Return a pointer to the newest command in the history */ 410 char ** 411 hist_get_newest(bool allow_cur) 412 { 413 if (histptr < history || (!allow_cur && histptr == history)) { 414 bi_errorf("no history (yet)"); 415 return (NULL); 416 } 417 return (allow_cur ? histptr : histptr - 1); 418 } 419 420 /* Return a pointer to the oldest command in the history */ 421 static char ** 422 hist_get_oldest(void) 423 { 424 if (histptr <= history) { 425 bi_errorf("no history (yet)"); 426 return (NULL); 427 } 428 return (history); 429 } 430 431 /******************************/ 432 /* Back up over last histsave */ 433 /******************************/ 434 static void 435 histbackup(void) 436 { 437 static int last_line = -1; 438 439 if (histptr >= history && last_line != hist_source->line) { 440 hist_source->line--; 441 afree(*histptr, APERM); 442 histptr--; 443 last_line = hist_source->line; 444 } 445 } 446 447 /* 448 * Return the current position. 449 */ 450 char ** 451 histpos(void) 452 { 453 return (current); 454 } 455 456 int 457 histnum(int n) 458 { 459 int last = histptr - history; 460 461 if (n < 0 || n >= last) { 462 current = histptr; 463 return (last); 464 } else { 465 current = &history[n]; 466 return (n); 467 } 468 } 469 470 /* 471 * This will become unnecessary if hist_get is modified to allow 472 * searching from positions other than the end, and in either 473 * direction. 474 */ 475 int 476 findhist(int start, int fwd, const char *str, int anchored) 477 { 478 char **hp; 479 int maxhist = histptr - history; 480 int incr = fwd ? 1 : -1; 481 int len = strlen(str); 482 483 if (start < 0 || start >= maxhist) 484 start = maxhist; 485 486 hp = &history[start]; 487 for (; hp >= history && hp <= histptr; hp += incr) 488 if ((anchored && strncmp(*hp, str, len) == 0) || 489 (!anchored && strstr(*hp, str))) 490 return (hp - history); 491 492 return (-1); 493 } 494 495 int 496 findhistrel(const char *str) 497 { 498 int maxhist = histptr - history; 499 int start = maxhist - 1; 500 int rec; 501 502 getn(str, &rec); 503 if (rec == 0) 504 return (-1); 505 if (rec > 0) { 506 if (rec > maxhist) 507 return (-1); 508 return (rec - 1); 509 } 510 if (rec > maxhist) 511 return (-1); 512 return (start + rec + 1); 513 } 514 515 /* 516 * set history 517 * this means reallocating the dataspace 518 */ 519 void 520 sethistsize(int n) 521 { 522 if (n > 0 && n != histsize) { 523 int cursize = histptr - history; 524 525 /* save most recent history */ 526 if (n < cursize) { 527 memmove(history, histptr - n, n * sizeof(char *)); 528 cursize = n; 529 } 530 531 history = aresize(history, n * sizeof(char *), APERM); 532 533 histsize = n; 534 histptr = history + cursize; 535 } 536 } 537 538 #if HAVE_PERSISTENT_HISTORY 539 /* 540 * set history file 541 * This can mean reloading/resetting/starting history file 542 * maintenance 543 */ 544 void 545 sethistfile(const char *name) 546 { 547 /* if not started then nothing to do */ 548 if (hstarted == 0) 549 return; 550 551 /* if the name is the same as the name we have */ 552 if (hname && strcmp(hname, name) == 0) 553 return; 554 555 /* 556 * its a new name - possibly 557 */ 558 if (histfd) { 559 /* yes the file is open */ 560 (void)close(histfd); 561 histfd = 0; 562 hsize = 0; 563 afree(hname, APERM); 564 hname = NULL; 565 /* let's reset the history */ 566 histptr = history - 1; 567 hist_source->line = 0; 568 } 569 570 hist_init(hist_source); 571 } 572 #endif 573 574 /* 575 * initialise the history vector 576 */ 577 void 578 init_histvec(void) 579 { 580 if (history == (char **)NULL) { 581 histsize = HISTORYSIZE; 582 history = alloc(histsize * sizeof(char *), APERM); 583 histptr = history - 1; 584 } 585 } 586 587 588 /* 589 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to 590 * a) permit HISTSIZE to control number of lines of history stored 591 * b) maintain a physical history file 592 * 593 * It turns out that there is a lot of ghastly hackery here 594 */ 595 596 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY 597 /* do not save command in history but possibly sync */ 598 bool 599 histsync(void) 600 { 601 bool changed = false; 602 603 if (histfd) { 604 int lno = hist_source->line; 605 606 hist_source->line++; 607 writehistfile(0, NULL); 608 hist_source->line--; 609 610 if (lno != hist_source->line) 611 changed = true; 612 } 613 614 return (changed); 615 } 616 #endif 617 618 /* 619 * save command in history 620 */ 621 void 622 histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups) 623 { 624 char **hp; 625 char *c, *cp; 626 627 strdupx(c, cmd, APERM); 628 if ((cp = strchr(c, '\n')) != NULL) 629 *cp = '\0'; 630 631 if (ignoredups && !strcmp(c, *histptr) 632 #if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY 633 && !histsync() 634 #endif 635 ) { 636 afree(c, APERM); 637 return; 638 } 639 ++*lnp; 640 641 #if HAVE_PERSISTENT_HISTORY 642 if (histfd && dowrite) 643 writehistfile(*lnp, c); 644 #endif 645 646 hp = histptr; 647 648 if (++hp >= history + histsize) { /* remove oldest command */ 649 afree(*history, APERM); 650 for (hp = history; hp < history + histsize - 1; hp++) 651 hp[0] = hp[1]; 652 } 653 *hp = c; 654 histptr = hp; 655 } 656 657 /* 658 * Write history data to a file nominated by HISTFILE 659 * if HISTFILE is unset then history still happens, but 660 * the data is not written to a file 661 * All copies of ksh looking at the file will maintain the 662 * same history. This is ksh behaviour. 663 * 664 * This stuff uses mmap() 665 * if your system ain't got it - then you'll have to undef HISTORYFILE 666 */ 667 668 /* 669 * Open a history file 670 * Format is: 671 * Bytes 1, 2: 672 * HMAGIC - just to check that we are dealing with 673 * the correct object 674 * Then follows a number of stored commands 675 * Each command is 676 * <command byte><command number(4 bytes)><bytes><null> 677 */ 678 #define HMAGIC1 0xab 679 #define HMAGIC2 0xcd 680 #define COMMAND 0xff 681 682 void 683 hist_init(Source *s) 684 { 685 #if HAVE_PERSISTENT_HISTORY 686 unsigned char *base; 687 int lines, fd, rv = 0; 688 #endif 689 690 if (Flag(FTALKING) == 0) 691 return; 692 693 hstarted = 1; 694 695 hist_source = s; 696 697 #if HAVE_PERSISTENT_HISTORY 698 if ((hname = str_val(global("HISTFILE"))) == NULL) 699 return; 700 strdupx(hname, hname, APERM); 701 702 retry: 703 /* we have a file and are interactive */ 704 if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0) 705 return; 706 707 histfd = savefd(fd); 708 if (histfd != fd) 709 close(fd); 710 711 (void)flock(histfd, LOCK_EX); 712 713 hsize = lseek(histfd, (off_t)0, SEEK_END); 714 715 if (hsize == 0) { 716 /* add magic */ 717 if (sprinkle(histfd)) { 718 hist_finish(); 719 return; 720 } 721 } else if (hsize > 0) { 722 /* 723 * we have some data 724 */ 725 base = (void *)mmap(NULL, hsize, PROT_READ, 726 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 727 /* 728 * check on its validity 729 */ 730 if (base == (unsigned char *)MAP_FAILED || 731 *base != HMAGIC1 || base[1] != HMAGIC2) { 732 if (base != (unsigned char *)MAP_FAILED) 733 munmap((caddr_t)base, hsize); 734 hist_finish(); 735 if (unlink(hname) /* fails */) 736 goto hiniterr; 737 goto retry; 738 } 739 if (hsize > 2) { 740 lines = hist_count_lines(base+2, hsize-2); 741 if (lines > histsize) { 742 /* we need to make the file smaller */ 743 if (hist_shrink(base, hsize)) 744 rv = unlink(hname); 745 munmap((caddr_t)base, hsize); 746 hist_finish(); 747 if (rv) { 748 hiniterr: 749 bi_errorf("cannot unlink HISTFILE %s" 750 " - %s", hname, strerror(errno)); 751 hsize = 0; 752 return; 753 } 754 goto retry; 755 } 756 } 757 histload(hist_source, base+2, hsize-2); 758 munmap((caddr_t)base, hsize); 759 } 760 (void)flock(histfd, LOCK_UN); 761 hsize = lseek(histfd, (off_t)0, SEEK_END); 762 #endif 763 } 764 765 #if HAVE_PERSISTENT_HISTORY 766 typedef enum state { 767 shdr, /* expecting a header */ 768 sline, /* looking for a null byte to end the line */ 769 sn1, /* bytes 1 to 4 of a line no */ 770 sn2, sn3, sn4 771 } State; 772 773 static int 774 hist_count_lines(unsigned char *base, int bytes) 775 { 776 State state = shdr; 777 int lines = 0; 778 779 while (bytes--) { 780 switch (state) { 781 case shdr: 782 if (*base == COMMAND) 783 state = sn1; 784 break; 785 case sn1: 786 state = sn2; break; 787 case sn2: 788 state = sn3; break; 789 case sn3: 790 state = sn4; break; 791 case sn4: 792 state = sline; break; 793 case sline: 794 if (*base == '\0') { 795 lines++; 796 state = shdr; 797 } 798 } 799 base++; 800 } 801 return (lines); 802 } 803 804 /* 805 * Shrink the history file to histsize lines 806 */ 807 static int 808 hist_shrink(unsigned char *oldbase, int oldbytes) 809 { 810 int fd, rv = 0; 811 char *nfile = NULL; 812 struct stat statb; 813 unsigned char *nbase = oldbase; 814 int nbytes = oldbytes; 815 816 nbase = hist_skip_back(nbase, &nbytes, histsize); 817 if (nbase == NULL) 818 return (1); 819 if (nbase == oldbase) 820 return (0); 821 822 /* 823 * create temp file 824 */ 825 nfile = shf_smprintf("%s.%d", hname, (int)procpid); 826 if ((fd = open(nfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) 827 goto errout; 828 if (fstat(histfd, &statb) >= 0 && 829 chown(nfile, statb.st_uid, statb.st_gid)) 830 goto errout; 831 832 if (sprinkle(fd) || write(fd, nbase, nbytes) != nbytes) 833 goto errout; 834 close(fd); 835 fd = -1; 836 837 /* 838 * rename 839 */ 840 if (rename(nfile, hname) < 0) { 841 errout: 842 if (fd >= 0) { 843 close(fd); 844 if (nfile) 845 unlink(nfile); 846 } 847 rv = 1; 848 } 849 afree(nfile, ATEMP); 850 return (rv); 851 } 852 853 /* 854 * find a pointer to the data 'no' back from the end of the file 855 * return the pointer and the number of bytes left 856 */ 857 static unsigned char * 858 hist_skip_back(unsigned char *base, int *bytes, int no) 859 { 860 int lines = 0; 861 unsigned char *ep; 862 863 for (ep = base + *bytes; --ep > base; ) { 864 /* 865 * this doesn't really work: the 4 byte line number that 866 * is encoded after the COMMAND byte can itself contain 867 * the COMMAND byte.... 868 */ 869 for (; ep > base && *ep != COMMAND; ep--) 870 ; 871 if (ep == base) 872 break; 873 if (++lines == no) { 874 *bytes = *bytes - ((char *)ep - (char *)base); 875 return (ep); 876 } 877 } 878 return (NULL); 879 } 880 881 /* 882 * load the history structure from the stored data 883 */ 884 static void 885 histload(Source *s, unsigned char *base, int bytes) 886 { 887 State state; 888 int lno = 0; 889 unsigned char *line = NULL; 890 891 for (state = shdr; bytes-- > 0; base++) { 892 switch (state) { 893 case shdr: 894 if (*base == COMMAND) 895 state = sn1; 896 break; 897 case sn1: 898 lno = (((*base)&0xff)<<24); 899 state = sn2; 900 break; 901 case sn2: 902 lno |= (((*base)&0xff)<<16); 903 state = sn3; 904 break; 905 case sn3: 906 lno |= (((*base)&0xff)<<8); 907 state = sn4; 908 break; 909 case sn4: 910 lno |= (*base)&0xff; 911 line = base+1; 912 state = sline; 913 break; 914 case sline: 915 if (*base == '\0') { 916 /* worry about line numbers */ 917 if (histptr >= history && lno-1 != s->line) { 918 /* a replacement ? */ 919 histinsert(s, lno, (char *)line); 920 } else { 921 s->line = lno--; 922 histsave(&lno, (char *)line, false, 923 false); 924 } 925 state = shdr; 926 } 927 } 928 } 929 } 930 931 /* 932 * Insert a line into the history at a specified number 933 */ 934 static void 935 histinsert(Source *s, int lno, const char *line) 936 { 937 char **hp; 938 939 if (lno >= s->line - (histptr - history) && lno <= s->line) { 940 hp = &histptr[lno - s->line]; 941 if (*hp) 942 afree(*hp, APERM); 943 strdupx(*hp, line, APERM); 944 } 945 } 946 947 /* 948 * write a command to the end of the history file 949 * This *MAY* seem easy but it's also necessary to check 950 * that the history file has not changed in size. 951 * If it has - then some other shell has written to it 952 * and we should read those commands to update our history 953 */ 954 static void 955 writehistfile(int lno, char *cmd) 956 { 957 int sizenow; 958 unsigned char *base; 959 unsigned char *news; 960 int bytes; 961 unsigned char hdr[5]; 962 963 (void)flock(histfd, LOCK_EX); 964 sizenow = lseek(histfd, (off_t)0, SEEK_END); 965 if (sizenow != hsize) { 966 /* 967 * Things have changed 968 */ 969 if (sizenow > hsize) { 970 /* someone has added some lines */ 971 bytes = sizenow - hsize; 972 base = (void *)mmap(NULL, sizenow, PROT_READ, 973 MAP_FILE | MAP_PRIVATE, histfd, (off_t)0); 974 if (base == (unsigned char *)MAP_FAILED) 975 goto bad; 976 news = base + hsize; 977 if (*news != COMMAND) { 978 munmap((caddr_t)base, sizenow); 979 goto bad; 980 } 981 hist_source->line--; 982 histload(hist_source, news, bytes); 983 hist_source->line++; 984 lno = hist_source->line; 985 munmap((caddr_t)base, sizenow); 986 hsize = sizenow; 987 } else { 988 /* it has shrunk */ 989 /* but to what? */ 990 /* we'll give up for now */ 991 goto bad; 992 } 993 } 994 if (cmd) { 995 /* 996 * we can write our bit now 997 */ 998 hdr[0] = COMMAND; 999 hdr[1] = (lno>>24)&0xff; 1000 hdr[2] = (lno>>16)&0xff; 1001 hdr[3] = (lno>>8)&0xff; 1002 hdr[4] = lno&0xff; 1003 bytes = strlen(cmd) + 1; 1004 if ((write(histfd, hdr, 5) != 5) || 1005 (write(histfd, cmd, bytes) != bytes)) 1006 goto bad; 1007 hsize = lseek(histfd, (off_t)0, SEEK_END); 1008 } 1009 (void)flock(histfd, LOCK_UN); 1010 return; 1011 bad: 1012 hist_finish(); 1013 } 1014 1015 void 1016 hist_finish(void) 1017 { 1018 (void)flock(histfd, LOCK_UN); 1019 (void)close(histfd); 1020 histfd = 0; 1021 } 1022 1023 /* 1024 * add magic to the history file 1025 */ 1026 static int 1027 sprinkle(int fd) 1028 { 1029 static const unsigned char mag[] = { HMAGIC1, HMAGIC2 }; 1030 1031 return (write(fd, mag, 2) != 2); 1032 } 1033 #endif 1034 1035 #if !HAVE_SYS_SIGNAME 1036 static const struct mksh_sigpair { 1037 const char *const name; 1038 int nr; 1039 } mksh_sigpairs[] = { 1040 #include "signames.inc" 1041 { NULL, 0 } 1042 }; 1043 #endif 1044 1045 void 1046 inittraps(void) 1047 { 1048 int i; 1049 const char *cs; 1050 1051 /* Populate sigtraps based on sys_signame and sys_siglist. */ 1052 for (i = 0; i <= NSIG; i++) { 1053 sigtraps[i].signal = i; 1054 if (i == SIGERR_) { 1055 sigtraps[i].name = "ERR"; 1056 sigtraps[i].mess = "Error handler"; 1057 } else { 1058 #if HAVE_SYS_SIGNAME 1059 cs = sys_signame[i]; 1060 #else 1061 const struct mksh_sigpair *pair = mksh_sigpairs; 1062 while ((pair->nr != i) && (pair->name != NULL)) 1063 ++pair; 1064 cs = pair->name; 1065 #endif 1066 if ((cs == NULL) || 1067 (cs[0] == '\0')) 1068 sigtraps[i].name = shf_smprintf("%d", i); 1069 else { 1070 char *s; 1071 1072 if (!strncasecmp(cs, "SIG", 3)) 1073 cs += 3; 1074 strdupx(s, cs, APERM); 1075 sigtraps[i].name = s; 1076 while ((*s = ksh_toupper(*s))) 1077 ++s; 1078 } 1079 #if HAVE_SYS_SIGLIST 1080 sigtraps[i].mess = sys_siglist[i]; 1081 #elif HAVE_STRSIGNAL 1082 sigtraps[i].mess = strsignal(i); 1083 #else 1084 sigtraps[i].mess = NULL; 1085 #endif 1086 if ((sigtraps[i].mess == NULL) || 1087 (sigtraps[i].mess[0] == '\0')) 1088 sigtraps[i].mess = shf_smprintf("Signal %d", i); 1089 } 1090 } 1091 sigtraps[SIGEXIT_].name = "EXIT"; /* our name for signal 0 */ 1092 1093 (void)sigemptyset(&Sigact_ign.sa_mask); 1094 Sigact_ign.sa_flags = 0; /* interruptible */ 1095 Sigact_ign.sa_handler = SIG_IGN; 1096 1097 sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1098 sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR; 1099 sigtraps[SIGTERM].flags |= TF_DFL_INTR;/* not fatal for interactive */ 1100 sigtraps[SIGHUP].flags |= TF_FATAL; 1101 sigtraps[SIGCHLD].flags |= TF_SHELL_USES; 1102 1103 /* these are always caught so we can clean up any temporary files. */ 1104 setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG); 1105 setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG); 1106 setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG); 1107 setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG); 1108 } 1109 1110 static void alarm_catcher(int sig); 1111 1112 void 1113 alarm_init(void) 1114 { 1115 sigtraps[SIGALRM].flags |= TF_SHELL_USES; 1116 setsig(&sigtraps[SIGALRM], alarm_catcher, 1117 SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP); 1118 } 1119 1120 /* ARGSUSED */ 1121 static void 1122 alarm_catcher(int sig MKSH_A_UNUSED) 1123 { 1124 /* this runs inside interrupt context, with errno saved */ 1125 1126 if (ksh_tmout_state == TMOUT_READING) { 1127 int left = alarm(0); 1128 1129 if (left == 0) { 1130 ksh_tmout_state = TMOUT_LEAVING; 1131 intrsig = 1; 1132 } else 1133 alarm(left); 1134 } 1135 } 1136 1137 Trap * 1138 gettrap(const char *name, int igncase) 1139 { 1140 int n = NSIG + 1; 1141 Trap *p; 1142 const char *n2; 1143 int (*cmpfunc)(const char *, const char *) = strcmp; 1144 1145 if (ksh_isdigit(*name)) { 1146 if (getn(name, &n) && 0 <= n && n < NSIG) 1147 return (&sigtraps[n]); 1148 else 1149 return (NULL); 1150 } 1151 1152 n2 = strncasecmp(name, "SIG", 3) ? NULL : name + 3; 1153 if (igncase) 1154 cmpfunc = strcasecmp; 1155 for (p = sigtraps; --n >= 0; p++) 1156 if (!cmpfunc(p->name, name) || (n2 && !cmpfunc(p->name, n2))) 1157 return (p); 1158 return (NULL); 1159 } 1160 1161 /* 1162 * trap signal handler 1163 */ 1164 void 1165 trapsig(int i) 1166 { 1167 Trap *p = &sigtraps[i]; 1168 int errno_ = errno; 1169 1170 trap = p->set = 1; 1171 if (p->flags & TF_DFL_INTR) 1172 intrsig = 1; 1173 if ((p->flags & TF_FATAL) && !p->trap) { 1174 fatal_trap = 1; 1175 intrsig = 1; 1176 } 1177 if (p->shtrap) 1178 (*p->shtrap)(i); 1179 errno = errno_; 1180 } 1181 1182 /* 1183 * called when we want to allow the user to ^C out of something - won't 1184 * work if user has trapped SIGINT. 1185 */ 1186 void 1187 intrcheck(void) 1188 { 1189 if (intrsig) 1190 runtraps(TF_DFL_INTR|TF_FATAL); 1191 } 1192 1193 /* 1194 * called after EINTR to check if a signal with normally causes process 1195 * termination has been received. 1196 */ 1197 int 1198 fatal_trap_check(void) 1199 { 1200 int i; 1201 Trap *p; 1202 1203 /* todo: should check if signal is fatal, not the TF_DFL_INTR flag */ 1204 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 1205 if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) 1206 /* return value is used as an exit code */ 1207 return (128 + p->signal); 1208 return (0); 1209 } 1210 1211 /* 1212 * Returns the signal number of any pending traps: ie, a signal which has 1213 * occurred for which a trap has been set or for which the TF_DFL_INTR flag 1214 * is set. 1215 */ 1216 int 1217 trap_pending(void) 1218 { 1219 int i; 1220 Trap *p; 1221 1222 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 1223 if (p->set && ((p->trap && p->trap[0]) || 1224 ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap))) 1225 return (p->signal); 1226 return (0); 1227 } 1228 1229 /* 1230 * run any pending traps. If intr is set, only run traps that 1231 * can interrupt commands. 1232 */ 1233 void 1234 runtraps(int flag) 1235 { 1236 int i; 1237 Trap *p; 1238 1239 if (ksh_tmout_state == TMOUT_LEAVING) { 1240 ksh_tmout_state = TMOUT_EXECUTING; 1241 warningf(false, "timed out waiting for input"); 1242 unwind(LEXIT); 1243 } else 1244 /* 1245 * XXX: this means the alarm will have no effect if a trap 1246 * is caught after the alarm() was started...not good. 1247 */ 1248 ksh_tmout_state = TMOUT_EXECUTING; 1249 if (!flag) 1250 trap = 0; 1251 if (flag & TF_DFL_INTR) 1252 intrsig = 0; 1253 if (flag & TF_FATAL) 1254 fatal_trap = 0; 1255 for (p = sigtraps, i = NSIG+1; --i >= 0; p++) 1256 if (p->set && (!flag || 1257 ((p->flags & flag) && p->trap == NULL))) 1258 runtrap(p); 1259 } 1260 1261 void 1262 runtrap(Trap *p) 1263 { 1264 int i = p->signal; 1265 char *trapstr = p->trap; 1266 int oexstat; 1267 int old_changed = 0; 1268 1269 p->set = 0; 1270 if (trapstr == NULL) { /* SIG_DFL */ 1271 if (p->flags & TF_FATAL) { 1272 /* eg, SIGHUP */ 1273 exstat = 128 + i; 1274 unwind(LLEAVE); 1275 } 1276 if (p->flags & TF_DFL_INTR) { 1277 /* eg, SIGINT, SIGQUIT, SIGTERM, etc. */ 1278 exstat = 128 + i; 1279 unwind(LINTR); 1280 } 1281 return; 1282 } 1283 if (trapstr[0] == '\0') /* SIG_IGN */ 1284 return; 1285 if (i == SIGEXIT_ || i == SIGERR_) { /* avoid recursion on these */ 1286 old_changed = p->flags & TF_CHANGED; 1287 p->flags &= ~TF_CHANGED; 1288 p->trap = NULL; 1289 } 1290 oexstat = exstat; 1291 /* 1292 * Note: trapstr is fully parsed before anything is executed, thus 1293 * no problem with afree(p->trap) in settrap() while still in use. 1294 */ 1295 command(trapstr, current_lineno); 1296 exstat = oexstat; 1297 if (i == SIGEXIT_ || i == SIGERR_) { 1298 if (p->flags & TF_CHANGED) 1299 /* don't clear TF_CHANGED */ 1300 afree(trapstr, APERM); 1301 else 1302 p->trap = trapstr; 1303 p->flags |= old_changed; 1304 } 1305 } 1306 1307 /* clear pending traps and reset user's trap handlers; used after fork(2) */ 1308 void 1309 cleartraps(void) 1310 { 1311 int i; 1312 Trap *p; 1313 1314 trap = 0; 1315 intrsig = 0; 1316 fatal_trap = 0; 1317 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) { 1318 p->set = 0; 1319 if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0])) 1320 settrap(p, NULL); 1321 } 1322 } 1323 1324 /* restore signals just before an exec(2) */ 1325 void 1326 restoresigs(void) 1327 { 1328 int i; 1329 Trap *p; 1330 1331 for (i = NSIG+1, p = sigtraps; --i >= 0; p++) 1332 if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL)) 1333 setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL, 1334 SS_RESTORE_CURR|SS_FORCE); 1335 } 1336 1337 void 1338 settrap(Trap *p, const char *s) 1339 { 1340 sig_t f; 1341 1342 if (p->trap) 1343 afree(p->trap, APERM); 1344 strdupx(p->trap, s, APERM); /* handles s == 0 */ 1345 p->flags |= TF_CHANGED; 1346 f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN; 1347 1348 p->flags |= TF_USER_SET; 1349 if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL) 1350 f = trapsig; 1351 else if (p->flags & TF_SHELL_USES) { 1352 if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) { 1353 /* do what user wants at exec time */ 1354 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1355 if (f == SIG_IGN) 1356 p->flags |= TF_EXEC_IGN; 1357 else 1358 p->flags |= TF_EXEC_DFL; 1359 } 1360 1361 /* 1362 * assumes handler already set to what shell wants it 1363 * (normally trapsig, but could be j_sigchld() or SIG_IGN) 1364 */ 1365 return; 1366 } 1367 1368 /* todo: should we let user know signal is ignored? how? */ 1369 setsig(p, f, SS_RESTORE_CURR|SS_USER); 1370 } 1371 1372 /* 1373 * Called by c_print() when writing to a co-process to ensure SIGPIPE won't 1374 * kill shell (unless user catches it and exits) 1375 */ 1376 int 1377 block_pipe(void) 1378 { 1379 int restore_dfl = 0; 1380 Trap *p = &sigtraps[SIGPIPE]; 1381 1382 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1383 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1384 if (p->flags & TF_ORIG_DFL) 1385 restore_dfl = 1; 1386 } else if (p->cursig == SIG_DFL) { 1387 setsig(p, SIG_IGN, SS_RESTORE_CURR); 1388 restore_dfl = 1; /* restore to SIG_DFL */ 1389 } 1390 return (restore_dfl); 1391 } 1392 1393 /* Called by c_print() to undo whatever block_pipe() did */ 1394 void 1395 restore_pipe(int restore_dfl) 1396 { 1397 if (restore_dfl) 1398 setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR); 1399 } 1400 1401 /* 1402 * Set action for a signal. Action may not be set if original 1403 * action was SIG_IGN, depending on the value of flags and FTALKING. 1404 */ 1405 int 1406 setsig(Trap *p, sig_t f, int flags) 1407 { 1408 struct sigaction sigact; 1409 1410 if (p->signal == SIGEXIT_ || p->signal == SIGERR_) 1411 return (1); 1412 1413 /* 1414 * First time setting this signal? If so, get and note the current 1415 * setting. 1416 */ 1417 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) { 1418 sigaction(p->signal, &Sigact_ign, &sigact); 1419 p->flags |= sigact.sa_handler == SIG_IGN ? 1420 TF_ORIG_IGN : TF_ORIG_DFL; 1421 p->cursig = SIG_IGN; 1422 } 1423 1424 /*- 1425 * Generally, an ignored signal stays ignored, except if 1426 * - the user of an interactive shell wants to change it 1427 * - the shell wants for force a change 1428 */ 1429 if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) && 1430 (!(flags & SS_USER) || !Flag(FTALKING))) 1431 return (0); 1432 1433 setexecsig(p, flags & SS_RESTORE_MASK); 1434 1435 /* 1436 * This is here 'cause there should be a way of clearing 1437 * shtraps, but don't know if this is a sane way of doing 1438 * it. At the moment, all users of shtrap are lifetime 1439 * users (SIGALRM, SIGCHLD, SIGWINCH). 1440 */ 1441 if (!(flags & SS_USER)) 1442 p->shtrap = (sig_t)NULL; 1443 if (flags & SS_SHTRAP) { 1444 p->shtrap = f; 1445 f = trapsig; 1446 } 1447 1448 if (p->cursig != f) { 1449 p->cursig = f; 1450 (void)sigemptyset(&sigact.sa_mask); 1451 sigact.sa_flags = 0 /* interruptible */; 1452 sigact.sa_handler = f; 1453 sigaction(p->signal, &sigact, NULL); 1454 } 1455 1456 return (1); 1457 } 1458 1459 /* control what signal is set to before an exec() */ 1460 void 1461 setexecsig(Trap *p, int restore) 1462 { 1463 /* XXX debugging */ 1464 if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) 1465 internal_errorf("setexecsig: unset signal %d(%s)", 1466 p->signal, p->name); 1467 1468 /* restore original value for exec'd kids */ 1469 p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL); 1470 switch (restore & SS_RESTORE_MASK) { 1471 case SS_RESTORE_CURR: /* leave things as they currently are */ 1472 break; 1473 case SS_RESTORE_ORIG: 1474 p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL; 1475 break; 1476 case SS_RESTORE_DFL: 1477 p->flags |= TF_EXEC_DFL; 1478 break; 1479 case SS_RESTORE_IGN: 1480 p->flags |= TF_EXEC_IGN; 1481 break; 1482 } 1483 } 1484