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