1 /* $NetBSD: history.c,v 1.9 2005/06/26 19:09:00 christos Exp $ */ 2 3 /* 4 * command history 5 * 6 * only implements in-memory history. 7 */ 8 9 /* 10 * This file contains 11 * a) the original in-memory history mechanism 12 * b) a simple file saving history mechanism done by sjg@zen 13 * define EASY_HISTORY to get this 14 * c) a more complicated mechanism done by pc@hillside.co.uk 15 * that more closely follows the real ksh way of doing 16 * things. You need to have the mmap system call for this 17 * to work on your system 18 */ 19 #include <sys/cdefs.h> 20 21 #ifndef lint 22 __RCSID("$NetBSD: history.c,v 1.9 2005/06/26 19:09:00 christos Exp $"); 23 #endif 24 25 26 #include "sh.h" 27 #include "ksh_stat.h" 28 29 #ifdef HISTORY 30 # ifdef EASY_HISTORY 31 32 # ifndef HISTFILE 33 # ifdef OS2 34 # define HISTFILE "history.ksh" 35 # else /* OS2 */ 36 # define HISTFILE ".pdksh_history" 37 # endif /* OS2 */ 38 # endif 39 40 # else 41 /* Defines and includes for the complicated case */ 42 43 # include <sys/file.h> 44 # include <sys/mman.h> 45 46 /* 47 * variables for handling the data file 48 */ 49 static int histfd; 50 static int hsize; 51 52 static int hist_count_lines ARGS((unsigned char *, int)); 53 static int hist_shrink ARGS((unsigned char *, int)); 54 static unsigned char *hist_skip_back ARGS((unsigned char *,int *,int)); 55 static void histload ARGS((Source *, unsigned char *, int)); 56 static void histinsert ARGS((Source *, int, unsigned char *)); 57 static void writehistfile ARGS((int, char *)); 58 static int sprinkle ARGS((int)); 59 60 # ifdef MAP_FILE 61 # define MAP_FLAGS (MAP_FILE|MAP_PRIVATE) 62 # else 63 # define MAP_FLAGS MAP_PRIVATE 64 # endif 65 66 # endif /* of EASY_HISTORY */ 67 68 static int hist_execute ARGS((char *)); 69 static int hist_replace ARGS((char **, const char *, const char *, int)); 70 static char **hist_get ARGS((const char *, int, int)); 71 static char **hist_get_newest ARGS((int)); 72 static char **hist_get_oldest ARGS((void)); 73 static void histbackup ARGS((void)); 74 75 static char **current; /* current position in history[] */ 76 static int curpos; /* current index in history[] */ 77 static char *hname; /* current name of history file */ 78 static int hstarted; /* set after hist_init() called */ 79 static Source *hist_source; 80 81 82 int 83 c_fc(wp) 84 char **wp; 85 { 86 struct shf *shf; 87 struct temp UNINITIALIZED(*tf); 88 char *p, *editor = (char *) 0; 89 int gflag = 0, lflag = 0, nflag = 0, sflag = 0, rflag = 0; 90 int optc; 91 char *first = (char *) 0, *last = (char *) 0; 92 char **hfirst, **hlast, **hp; 93 94 if (hist_source == NULL) { 95 bi_errorf("not interactive"); 96 return 1; 97 } 98 99 while ((optc = ksh_getopt(wp, &builtin_opt, "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != EOF) 100 switch (optc) { 101 case 'e': 102 p = builtin_opt.optarg; 103 if (strcmp(p, "-") == 0) 104 sflag++; 105 else { 106 size_t len = strlen(p) + 4; 107 editor = str_nsave(p, len, ATEMP); 108 strlcat(editor, " $_", len); 109 } 110 break; 111 case 'g': /* non-at&t ksh */ 112 gflag++; 113 break; 114 case 'l': 115 lflag++; 116 break; 117 case 'n': 118 nflag++; 119 break; 120 case 'r': 121 rflag++; 122 break; 123 case 's': /* posix version of -e - */ 124 sflag++; 125 break; 126 /* kludge city - accept -num as -- -num (kind of) */ 127 case '0': case '1': case '2': case '3': case '4': 128 case '5': case '6': case '7': case '8': case '9': 129 p = shf_smprintf("-%c%s", 130 optc, builtin_opt.optarg); 131 if (!first) 132 first = p; 133 else if (!last) 134 last = p; 135 else { 136 bi_errorf("too many arguments"); 137 return 1; 138 } 139 break; 140 case '?': 141 return 1; 142 } 143 wp += builtin_opt.optind; 144 145 /* Substitute and execute command */ 146 if (sflag) { 147 char *pat = (char *) 0, *rep = (char *) 0; 148 149 if (editor || lflag || nflag || rflag) { 150 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)"); 151 return 1; 152 } 153 154 /* Check for pattern replacement argument */ 155 if (*wp && **wp && (p = strchr(*wp + 1, '='))) { 156 pat = str_save(*wp, ATEMP); 157 p = pat + (p - *wp); 158 *p++ = '\0'; 159 rep = p; 160 wp++; 161 } 162 /* Check for search prefix */ 163 if (!first && (first = *wp)) 164 wp++; 165 if (last || *wp) { 166 bi_errorf("too many arguments"); 167 return 1; 168 } 169 170 hp = first ? hist_get(first, FALSE, FALSE) 171 : hist_get_newest(FALSE); 172 if (!hp) 173 return 1; 174 return hist_replace(hp, pat, rep, gflag); 175 } 176 177 if (editor && (lflag || nflag)) { 178 bi_errorf("can't use -l, -n with -e"); 179 return 1; 180 } 181 182 if (!first && (first = *wp)) 183 wp++; 184 if (!last && (last = *wp)) 185 wp++; 186 if (*wp) { 187 bi_errorf("too many arguments"); 188 return 1; 189 } 190 if (!first) { 191 hfirst = lflag ? hist_get("-16", TRUE, TRUE) 192 : hist_get_newest(FALSE); 193 if (!hfirst) 194 return 1; 195 /* can't fail if hfirst didn't fail */ 196 hlast = hist_get_newest(FALSE); 197 } else { 198 /* POSIX says not an error if first/last out of bounds 199 * when range is specified; at&t ksh and pdksh allow out of 200 * bounds for -l as well. 201 */ 202 hfirst = hist_get(first, (lflag || last) ? TRUE : FALSE, 203 lflag ? TRUE : FALSE); 204 if (!hfirst) 205 return 1; 206 hlast = last ? hist_get(last, TRUE, lflag ? TRUE : FALSE) 207 : (lflag ? hist_get_newest(FALSE) : hfirst); 208 if (!hlast) 209 return 1; 210 } 211 if (hfirst > hlast) { 212 char **temp; 213 214 temp = hfirst; hfirst = hlast; hlast = temp; 215 rflag = !rflag; /* POSIX */ 216 } 217 218 /* List history */ 219 if (lflag) { 220 char *s, *t; 221 const char *nfmt = nflag ? "\t" : "%d\t"; 222 223 for (hp = rflag ? hlast : hfirst; 224 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) 225 { 226 shf_fprintf(shl_stdout, nfmt, 227 hist_source->line - (int) (histptr - hp)); 228 /* print multi-line commands correctly */ 229 for (s = *hp; (t = strchr(s, '\n')); s = t) 230 shf_fprintf(shl_stdout, "%.*s\t", ++t - s, s); 231 shf_fprintf(shl_stdout, "%s\n", s); 232 } 233 shf_flush(shl_stdout); 234 return 0; 235 } 236 237 /* Run editor on selected lines, then run resulting commands */ 238 239 tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps); 240 if (!(shf = tf->shf)) { 241 bi_errorf("cannot create temp file %s - %s", 242 tf->name, strerror(errno)); 243 return 1; 244 } 245 for (hp = rflag ? hlast : hfirst; 246 hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) 247 shf_fprintf(shf, "%s\n", *hp); 248 if (shf_close(shf) == EOF) { 249 bi_errorf("error writing temporary file - %s", strerror(errno)); 250 return 1; 251 } 252 253 /* Ignore setstr errors here (arbitrary) */ 254 setstr(local("_", FALSE), tf->name, KSH_RETURN_ERROR); 255 256 /* XXX: source should not get trashed by this.. */ 257 { 258 Source *sold = source; 259 int ret; 260 261 ret = command(editor ? editor : "${FCEDIT:-/bin/ed} $_"); 262 source = sold; 263 if (ret) 264 return ret; 265 } 266 267 { 268 struct stat statb; 269 XString xs; 270 char *xp; 271 int n; 272 273 if (!(shf = shf_open(tf->name, O_RDONLY, 0, 0))) { 274 bi_errorf("cannot open temp file %s", tf->name); 275 return 1; 276 } 277 278 n = fstat(shf_fileno(shf), &statb) < 0 ? 128 279 : statb.st_size + 1; 280 Xinit(xs, xp, n, hist_source->areap); 281 while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) { 282 xp += n; 283 if (Xnleft(xs, xp) <= 0) 284 XcheckN(xs, xp, Xlength(xs, xp)); 285 } 286 if (n < 0) { 287 bi_errorf("error reading temp file %s - %s", 288 tf->name, strerror(shf_errno(shf))); 289 shf_close(shf); 290 return 1; 291 } 292 shf_close(shf); 293 *xp = '\0'; 294 strip_nuls(Xstring(xs, xp), Xlength(xs, xp)); 295 return hist_execute(Xstring(xs, xp)); 296 } 297 } 298 299 /* Save cmd in history, execute cmd (cmd gets trashed) */ 300 static int 301 hist_execute(cmd) 302 char *cmd; 303 { 304 Source *sold; 305 int ret; 306 char *p, *q; 307 308 histbackup(); 309 310 for (p = cmd; p; p = q) { 311 if ((q = strchr(p, '\n'))) { 312 *q++ = '\0'; /* kill the newline */ 313 if (!*q) /* ignore trailing newline */ 314 q = (char *) 0; 315 } 316 #ifdef EASY_HISTORY 317 if (p != cmd) 318 histappend(p, TRUE); 319 else 320 #endif /* EASY_HISTORY */ 321 histsave(++(hist_source->line), p, 1); 322 323 shellf("%s\n", p); /* POSIX doesn't say this is done... */ 324 if ((p = q)) /* restore \n (trailing \n not restored) */ 325 q[-1] = '\n'; 326 } 327 328 /* Commands are executed here instead of pushing them onto the 329 * input 'cause posix says the redirection and variable assignments 330 * in 331 * X=y fc -e - 42 2> /dev/null 332 * are to effect the repeated commands environment. 333 */ 334 /* XXX: source should not get trashed by this.. */ 335 sold = source; 336 ret = command(cmd); 337 source = sold; 338 return ret; 339 } 340 341 static int 342 hist_replace(hp, pat, rep, globalv) 343 char **hp; 344 const char *pat; 345 const char *rep; 346 int globalv; 347 { 348 char *line; 349 350 if (!pat) 351 line = str_save(*hp, ATEMP); 352 else { 353 char *s, *s1; 354 int pat_len = strlen(pat); 355 int rep_len = strlen(rep); 356 int len; 357 XString xs; 358 char *xp; 359 int any_subst = 0; 360 361 Xinit(xs, xp, 128, ATEMP); 362 for (s = *hp; (s1 = strstr(s, pat)) 363 && (!any_subst || globalv) ; s = s1 + pat_len) 364 { 365 any_subst = 1; 366 len = s1 - s; 367 XcheckN(xs, xp, len + rep_len); 368 memcpy(xp, s, len); /* first part */ 369 xp += len; 370 memcpy(xp, rep, rep_len); /* replacement */ 371 xp += rep_len; 372 } 373 if (!any_subst) { 374 bi_errorf("substitution failed"); 375 return 1; 376 } 377 len = strlen(s) + 1; 378 XcheckN(xs, xp, len); 379 memcpy(xp, s, len); 380 xp += len; 381 line = Xclose(xs, xp); 382 } 383 return hist_execute(line); 384 } 385 386 /* 387 * get pointer to history given pattern 388 * pattern is a number or string 389 */ 390 static char ** 391 hist_get(str, approx, allow_cur) 392 const char *str; 393 int approx; 394 int allow_cur; 395 { 396 char **hp = (char **) 0; 397 int n; 398 399 if (getn(str, &n)) { 400 hp = histptr + (n < 0 ? n : (n - hist_source->line)); 401 if (hp < histlist) { 402 if (approx) 403 hp = hist_get_oldest(); 404 else { 405 bi_errorf("%s: not in history", str); 406 hp = (char **) 0; 407 } 408 } else if (hp > histptr) { 409 if (approx) 410 hp = hist_get_newest(allow_cur); 411 else { 412 bi_errorf("%s: not in history", str); 413 hp = (char **) 0; 414 } 415 } else if (!allow_cur && hp == histptr) { 416 bi_errorf("%s: invalid range", str); 417 hp = (char **) 0; 418 } 419 } else { 420 int anchored = *str == '?' ? (++str, 0) : 1; 421 422 /* the -1 is to avoid the current fc command */ 423 n = findhist(histptr - histlist - 1, 0, str, anchored); 424 if (n < 0) { 425 bi_errorf("%s: not in history", str); 426 hp = (char **) 0; 427 } else 428 hp = &histlist[n]; 429 } 430 return hp; 431 } 432 433 /* Return a pointer to the newest command in the history */ 434 static char ** 435 hist_get_newest(allow_cur) 436 int allow_cur; 437 { 438 if (histptr < histlist || (!allow_cur && histptr == histlist)) { 439 bi_errorf("no history (yet)"); 440 return (char **) 0; 441 } 442 if (allow_cur) 443 return histptr; 444 return histptr - 1; 445 } 446 447 /* Return a pointer to the newest command in the history */ 448 static char ** 449 hist_get_oldest() 450 { 451 if (histptr <= histlist) { 452 bi_errorf("no history (yet)"); 453 return (char **) 0; 454 } 455 return histlist; 456 } 457 458 /******************************/ 459 /* Back up over last histsave */ 460 /******************************/ 461 static void 462 histbackup() 463 { 464 static int last_line = -1; 465 466 if (histptr >= histlist && last_line != hist_source->line) { 467 hist_source->line--; 468 afree((void*)*histptr, APERM); 469 histptr--; 470 last_line = hist_source->line; 471 } 472 } 473 474 /* 475 * Return the current position. 476 */ 477 char ** 478 histpos() 479 { 480 return current; 481 } 482 483 int 484 histN() 485 { 486 return curpos; 487 } 488 489 int 490 histnum(n) 491 int n; 492 { 493 int last = histptr - histlist; 494 495 if (n < 0 || n >= last) { 496 current = histptr; 497 curpos = last; 498 return last; 499 } else { 500 current = &histlist[n]; 501 curpos = n; 502 return n; 503 } 504 } 505 506 /* 507 * This will become unnecessary if hist_get is modified to allow 508 * searching from positions other than the end, and in either 509 * direction. 510 */ 511 int 512 findhist(start, fwd, str, anchored) 513 int start; 514 int fwd; 515 const char *str; 516 int anchored; 517 { 518 char **hp; 519 int maxhist = histptr - histlist; 520 int incr = fwd ? 1 : -1; 521 int len = strlen(str); 522 523 if (start < 0 || start >= maxhist) 524 start = maxhist; 525 526 hp = &histlist[start]; 527 for (; hp >= histlist && hp <= histptr; hp += incr) 528 if ((anchored && strncmp(*hp, str, len) == 0) 529 || (!anchored && strstr(*hp, str))) 530 return hp - histlist; 531 532 return -1; 533 } 534 535 /* 536 * set history 537 * this means reallocating the dataspace 538 */ 539 void 540 sethistsize(n) 541 int n; 542 { 543 if (n > 0 && n != histsize) { 544 int cursize = histptr - histlist; 545 546 /* save most recent history */ 547 if (n < cursize) { 548 memmove(histlist, histptr - n, n * sizeof(char *)); 549 cursize = n; 550 } 551 552 histlist = (char **)aresize(histlist, n*sizeof(char *), APERM); 553 554 histsize = n; 555 histptr = histlist + cursize; 556 } 557 } 558 559 /* 560 * set history file 561 * This can mean reloading/resetting/starting history file 562 * maintenance 563 */ 564 void 565 sethistfile(name) 566 const char *name; 567 { 568 /* if not started then nothing to do */ 569 if (hstarted == 0) 570 return; 571 572 /* if the name is the same as the name we have */ 573 if (hname && strcmp(hname, name) == 0) 574 return; 575 576 /* 577 * its a new name - possibly 578 */ 579 # ifdef EASY_HISTORY 580 if (hname) { 581 afree(hname, APERM); 582 hname = NULL; 583 } 584 # else 585 if (histfd) { 586 /* yes the file is open */ 587 (void) close(histfd); 588 histfd = 0; 589 hsize = 0; 590 afree(hname, APERM); 591 hname = NULL; 592 /* let's reset the history */ 593 histptr = histlist - 1; 594 hist_source->line = 0; 595 } 596 # endif 597 598 hist_init(hist_source); 599 } 600 601 /* 602 * initialise the history vector 603 */ 604 void 605 init_histvec() 606 { 607 if (histlist == (char **)NULL) { 608 histsize = HISTORYSIZE; 609 histlist = (char **)alloc(histsize*sizeof (char *), APERM); 610 histptr = histlist - 1; 611 } 612 } 613 614 # ifdef EASY_HISTORY 615 /* 616 * save command in history 617 */ 618 void 619 histsave(lno, cmd, dowrite) 620 int lno; /* ignored (compatibility with COMPLEX_HISTORY) */ 621 const char *cmd; 622 int dowrite; /* ignored (compatibility with COMPLEX_HISTORY) */ 623 { 624 register char **hp = histptr; 625 char *cp; 626 627 if (++hp >= histlist + histsize) { /* remove oldest command */ 628 afree((void*)histlist[0], APERM); 629 memmove(histlist, histlist + 1, 630 sizeof(histlist[0]) * (histsize - 1)); 631 hp = &histlist[histsize - 1]; 632 } 633 *hp = str_save(cmd, APERM); 634 /* trash trailing newline but allow imbedded newlines */ 635 cp = *hp + strlen(*hp); 636 if (cp > *hp && cp[-1] == '\n') 637 cp[-1] = '\0'; 638 histptr = hp; 639 } 640 641 /* 642 * Append an entry to the last saved command. Used for multiline 643 * commands 644 */ 645 void 646 histappend(cmd, nl_separate) 647 const char *cmd; 648 int nl_separate; 649 { 650 int hlen, clen; 651 char *p; 652 653 hlen = strlen(*histptr); 654 clen = strlen(cmd); 655 if (clen > 0 && cmd[clen-1] == '\n') 656 clen--; 657 p = *histptr = (char *) aresize(*histptr, hlen + clen + 2, APERM); 658 p += hlen; 659 if (nl_separate) 660 *p++ = '\n'; 661 memcpy(p, cmd, clen); 662 p[clen] = '\0'; 663 } 664 665 /* 666 * 92-04-25 <sjg@zen> 667 * A simple history file implementation. 668 * At present we only save the history when we exit. 669 * This can cause problems when there are multiple shells are 670 * running under the same user-id. The last shell to exit gets 671 * to save its history. 672 */ 673 void 674 hist_init(s) 675 Source *s; 676 { 677 char *f; 678 FILE *fh; 679 680 if (Flag(FTALKING) == 0) 681 return; 682 683 hstarted = 1; 684 685 hist_source = s; 686 687 if ((f = str_val(global("HISTFILE"))) == NULL || *f == '\0') { 688 # if 1 /* Don't use history file unless the user asks for it */ 689 hname = NULL; 690 return; 691 # else 692 char *home = str_val(global("HOME")); 693 int len; 694 695 if (home == NULL) 696 home = null; 697 f = HISTFILE; 698 hname = alloc(len = strlen(home) + strlen(f) + 2, APERM); 699 shf_snprintf(hname, len, "%s/%s", home, f); 700 # endif 701 } else 702 hname = str_save(f, APERM); 703 704 if ((fh = fopen(hname, "r"))) { 705 int pos = 0, nread = 0; 706 int contin = 0; /* continuation of previous command */ 707 char *end; 708 char hline[LINE + 1]; 709 710 while (1) { 711 if (pos >= nread) { 712 pos = 0; 713 nread = fread(hline, 1, LINE, fh); 714 if (nread <= 0) 715 break; 716 hline[nread] = '\0'; 717 } 718 end = strchr(hline + pos, 0); /* will always succeed */ 719 if (contin) 720 histappend(hline + pos, 0); 721 else { 722 hist_source->line++; 723 histsave(0, hline + pos, 0); 724 } 725 pos = end - hline + 1; 726 contin = end == &hline[nread]; 727 } 728 fclose(fh); 729 } 730 } 731 732 /* 733 * save our history. 734 * We check that we do not have more than we are allowed. 735 * If the history file is read-only we do nothing. 736 * Handy for having all shells start with a useful history set. 737 */ 738 739 void 740 hist_finish() 741 { 742 static int once; 743 FILE *fh; 744 register int i; 745 register char **hp; 746 747 if (once++) 748 return; 749 /* check how many we have */ 750 i = histptr - histlist; 751 if (i >= histsize) 752 hp = &histptr[-histsize]; 753 else 754 hp = histlist; 755 if (hname && (fh = fopen(hname, "w"))) 756 { 757 for (i = 0; hp + i <= histptr && hp[i]; i++) 758 fprintf(fh, "%s%c", hp[i], '\0'); 759 fclose(fh); 760 } 761 } 762 763 # else /* EASY_HISTORY */ 764 765 /* 766 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to 767 * a) permit HISTSIZE to control number of lines of history stored 768 * b) maintain a physical history file 769 * 770 * It turns out that there is a lot of ghastly hackery here 771 */ 772 773 774 /* 775 * save command in history 776 */ 777 void 778 histsave(lno, cmd, dowrite) 779 int lno; 780 const char *cmd; 781 int dowrite; 782 { 783 register char **hp; 784 char *c, *cp; 785 786 c = str_save(cmd, APERM); 787 if ((cp = strchr(c, '\n')) != NULL) 788 *cp = '\0'; 789 790 if (histfd && dowrite) 791 writehistfile(lno, c); 792 793 hp = histptr; 794 795 if (++hp >= histlist + histsize) { /* remove oldest command */ 796 afree((void*)*histlist, APERM); 797 for (hp = histlist; hp < histlist + histsize - 1; hp++) 798 hp[0] = hp[1]; 799 } 800 *hp = c; 801 histptr = hp; 802 } 803 804 /* 805 * Write history data to a file nominated by HISTFILE 806 * if HISTFILE is unset then history still happens, but 807 * the data is not written to a file 808 * All copies of ksh looking at the file will maintain the 809 * same history. This is ksh behaviour. 810 * 811 * This stuff uses mmap() 812 * if your system ain't got it - then you'll have to undef HISTORYFILE 813 */ 814 815 /* 816 * Open a history file 817 * Format is: 818 * Bytes 1, 2: HMAGIC - just to check that we are dealing with 819 * the correct object 820 * Then follows a number of stored commands 821 * Each command is 822 * <command byte><command number(4 bytes)><bytes><null> 823 */ 824 # define HMAGIC1 0xab 825 # define HMAGIC2 0xcd 826 # define COMMAND 0xff 827 828 void 829 hist_init(s) 830 Source *s; 831 { 832 unsigned char *base; 833 int lines; 834 int fd; 835 836 if (Flag(FTALKING) == 0) 837 return; 838 839 hstarted = 1; 840 841 hist_source = s; 842 843 hname = str_val(global("HISTFILE")); 844 if (hname == NULL) 845 return; 846 hname = str_save(hname, APERM); 847 848 retry: 849 /* we have a file and are interactive */ 850 if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0) 851 return; 852 853 histfd = savefd(fd, 0); 854 855 (void) flock(histfd, LOCK_EX); 856 857 hsize = lseek(histfd, 0L, SEEK_END); 858 859 if (hsize == 0) { 860 /* add magic */ 861 if (sprinkle(histfd)) { 862 hist_finish(); 863 return; 864 } 865 } 866 else if (hsize > 0) { 867 /* 868 * we have some data 869 */ 870 base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0); 871 /* 872 * check on its validity 873 */ 874 if (base == MAP_FAILED || *base != HMAGIC1 || base[1] != HMAGIC2) { 875 if (base != MAP_FAILED) 876 munmap((caddr_t)base, hsize); 877 hist_finish(); 878 unlink(hname); 879 goto retry; 880 } 881 if (hsize > 2) { 882 lines = hist_count_lines(base+2, hsize-2); 883 if (lines > histsize) { 884 /* we need to make the file smaller */ 885 if (hist_shrink(base, hsize)) 886 unlink(hname); 887 munmap((caddr_t)base, hsize); 888 hist_finish(); 889 goto retry; 890 } 891 } 892 histload(hist_source, base+2, hsize-2); 893 munmap((caddr_t)base, hsize); 894 } 895 (void) flock(histfd, LOCK_UN); 896 hsize = lseek(histfd, 0L, SEEK_END); 897 } 898 899 typedef enum state { 900 shdr, /* expecting a header */ 901 sline, /* looking for a null byte to end the line */ 902 sn1, /* bytes 1 to 4 of a line no */ 903 sn2, sn3, sn4 904 } State; 905 906 static int 907 hist_count_lines(base, bytes) 908 register unsigned char *base; 909 register int bytes; 910 { 911 State state = shdr; 912 int lines = 0; 913 914 while (bytes--) { 915 switch (state) 916 { 917 case shdr: 918 if (*base == COMMAND) 919 state = sn1; 920 break; 921 case sn1: 922 state = sn2; break; 923 case sn2: 924 state = sn3; break; 925 case sn3: 926 state = sn4; break; 927 case sn4: 928 state = sline; break; 929 case sline: 930 if (*base == '\0') 931 lines++, state = shdr; 932 } 933 base++; 934 } 935 return lines; 936 } 937 938 /* 939 * Shrink the history file to histsize lines 940 */ 941 static int 942 hist_shrink(oldbase, oldbytes) 943 unsigned char *oldbase; 944 int oldbytes; 945 { 946 int fd; 947 char nfile[1024]; 948 struct stat statb; 949 unsigned char *nbase = oldbase; 950 int nbytes = oldbytes; 951 952 nbase = hist_skip_back(nbase, &nbytes, histsize); 953 if (nbase == NULL) 954 return 1; 955 if (nbase == oldbase) 956 return 0; 957 958 /* 959 * create temp file 960 */ 961 (void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid); 962 if ((fd = creat(nfile, 0600)) < 0) 963 return 1; 964 965 if (sprinkle(fd)) { 966 close(fd); 967 unlink(nfile); 968 return 1; 969 } 970 if (write(fd, nbase, nbytes) != nbytes) { 971 close(fd); 972 unlink(nfile); 973 return 1; 974 } 975 /* 976 * worry about who owns this file 977 */ 978 if (fstat(histfd, &statb) >= 0) 979 fchown(fd, statb.st_uid, statb.st_gid); 980 close(fd); 981 982 /* 983 * rename 984 */ 985 if (rename(nfile, hname) < 0) 986 return 1; 987 return 0; 988 } 989 990 991 /* 992 * find a pointer to the data `no' back from the end of the file 993 * return the pointer and the number of bytes left 994 */ 995 static unsigned char * 996 hist_skip_back(base, bytes, no) 997 unsigned char *base; 998 int *bytes; 999 int no; 1000 { 1001 register int lines = 0; 1002 register unsigned char *ep; 1003 1004 for (ep = base + *bytes; --ep > base; ) { 1005 /* this doesn't really work: the 4 byte line number that is 1006 * encoded after the COMMAND byte can itself contain the 1007 * COMMAND byte.... 1008 */ 1009 for (; ep > base && *ep != COMMAND; ep--) 1010 ; 1011 if (ep == base) 1012 break; 1013 if (++lines == no) { 1014 *bytes = *bytes - ((char *)ep - (char *)base); 1015 return ep; 1016 } 1017 } 1018 return NULL; 1019 } 1020 1021 /* 1022 * load the history structure from the stored data 1023 */ 1024 static void 1025 histload(s, base, bytes) 1026 Source *s; 1027 register unsigned char *base; 1028 register int bytes; 1029 { 1030 State state; 1031 int lno = 0; 1032 unsigned char *line = NULL; 1033 1034 for (state = shdr; bytes-- > 0; base++) { 1035 switch (state) { 1036 case shdr: 1037 if (*base == COMMAND) 1038 state = sn1; 1039 break; 1040 case sn1: 1041 lno = (((*base)&0xff)<<24); 1042 state = sn2; 1043 break; 1044 case sn2: 1045 lno |= (((*base)&0xff)<<16); 1046 state = sn3; 1047 break; 1048 case sn3: 1049 lno |= (((*base)&0xff)<<8); 1050 state = sn4; 1051 break; 1052 case sn4: 1053 lno |= (*base)&0xff; 1054 line = base+1; 1055 state = sline; 1056 break; 1057 case sline: 1058 if (*base == '\0') { 1059 /* worry about line numbers */ 1060 if (histptr >= histlist && lno-1 != s->line) { 1061 /* a replacement ? */ 1062 histinsert(s, lno, line); 1063 } 1064 else { 1065 s->line = lno; 1066 histsave(lno, (char *)line, 0); 1067 } 1068 state = shdr; 1069 } 1070 } 1071 } 1072 } 1073 1074 /* 1075 * Insert a line into the history at a specified number 1076 */ 1077 static void 1078 histinsert(s, lno, line) 1079 Source *s; 1080 int lno; 1081 unsigned char *line; 1082 { 1083 register char **hp; 1084 1085 if (lno >= s->line-(histptr-histlist) && lno <= s->line) { 1086 hp = &histptr[lno-s->line]; 1087 if (*hp) 1088 afree((void*)*hp, APERM); 1089 *hp = str_save((char *)line, APERM); 1090 } 1091 } 1092 1093 /* 1094 * write a command to the end of the history file 1095 * This *MAY* seem easy but it's also necessary to check 1096 * that the history file has not changed in size. 1097 * If it has - then some other shell has written to it 1098 * and we should read those commands to update our history 1099 */ 1100 static void 1101 writehistfile(lno, cmd) 1102 int lno; 1103 char *cmd; 1104 { 1105 int sizenow; 1106 unsigned char *base; 1107 unsigned char *new; 1108 int bytes; 1109 unsigned char hdr[5]; 1110 1111 (void) flock(histfd, LOCK_EX); 1112 sizenow = lseek(histfd, 0L, SEEK_END); 1113 if (sizenow != hsize) { 1114 /* 1115 * Things have changed 1116 */ 1117 if (sizenow > hsize) { 1118 /* someone has added some lines */ 1119 bytes = sizenow - hsize; 1120 base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0); 1121 if (base == MAP_FAILED) 1122 goto bad; 1123 new = base + hsize; 1124 if (*new != COMMAND) { 1125 munmap((caddr_t)base, sizenow); 1126 goto bad; 1127 } 1128 hist_source->line--; 1129 histload(hist_source, new, bytes); 1130 hist_source->line++; 1131 lno = hist_source->line; 1132 munmap((caddr_t)base, sizenow); 1133 hsize = sizenow; 1134 } else { 1135 /* it has shrunk */ 1136 /* but to what? */ 1137 /* we'll give up for now */ 1138 goto bad; 1139 } 1140 } 1141 /* 1142 * we can write our bit now 1143 */ 1144 hdr[0] = COMMAND; 1145 hdr[1] = (lno>>24)&0xff; 1146 hdr[2] = (lno>>16)&0xff; 1147 hdr[3] = (lno>>8)&0xff; 1148 hdr[4] = lno&0xff; 1149 (void) write(histfd, hdr, 5); 1150 (void) write(histfd, cmd, strlen(cmd)+1); 1151 hsize = lseek(histfd, 0L, SEEK_END); 1152 (void) flock(histfd, LOCK_UN); 1153 return; 1154 bad: 1155 hist_finish(); 1156 } 1157 1158 void 1159 hist_finish() 1160 { 1161 (void) flock(histfd, LOCK_UN); 1162 (void) close(histfd); 1163 histfd = 0; 1164 } 1165 1166 /* 1167 * add magic to the history file 1168 */ 1169 static int 1170 sprinkle(fd) 1171 int fd; 1172 { 1173 static unsigned char mag[] = { HMAGIC1, HMAGIC2 }; 1174 1175 return(write(fd, mag, 2) != 2); 1176 } 1177 1178 # endif 1179 #else /* HISTORY */ 1180 1181 /* No history to be compiled in: dummy routines to avoid lots more ifdefs */ 1182 void 1183 init_histvec() 1184 { 1185 } 1186 void 1187 hist_init(s) 1188 Source *s; 1189 { 1190 } 1191 void 1192 hist_finish() 1193 { 1194 } 1195 void 1196 histsave(lno, cmd, dowrite) 1197 int lno; 1198 const char *cmd; 1199 int dowrite; 1200 { 1201 errorf("history not enabled"); 1202 } 1203 #endif /* HISTORY */ 1204