1 /* $NetBSD: history.c,v 1.11 2011/08/31 16:24:54 plunky 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.11 2011/08/31 16:24:54 plunky 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 == 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 int fd; 744 #if defined(__minix) 745 /* LSC: FIXME: Minix doesn't implement O_EXLOCK yet. */ 746 int rc; 747 #endif 748 FILE *fh; 749 register int i; 750 register char **hp; 751 752 if (once++) 753 return; 754 if (hname == NULL || hname[0] == 0) 755 return; 756 757 /* check how many we have */ 758 i = histptr - histlist; 759 if (i >= histsize) 760 hp = &histptr[-histsize]; 761 else 762 hp = histlist; 763 764 fd = open(hname, O_WRONLY | O_CREAT | O_TRUNC | O_EXLOCK, 0777); 765 #if defined(__minix) 766 do { 767 rc = flock(fd, LOCK_EX); 768 if (rc == -1 && errno != EINTR) 769 return; 770 } while (rc == -1 && errno == EINTR); /* if interrupted, try again. */ 771 #endif 772 773 /* Remove anything written before we got the lock */ 774 ftruncate(fd, 0); 775 if (fd >= 0 && (fh = fdopen(fd, "w"))) { 776 for (i = 0; hp + i <= histptr && hp[i]; i++) 777 fprintf(fh, "%s%c", hp[i], '\0'); 778 #if defined(__minix) 779 flock(fd, LOCK_UN); 780 #endif 781 fclose(fh); 782 } 783 } 784 785 # else /* EASY_HISTORY */ 786 787 /* 788 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to 789 * a) permit HISTSIZE to control number of lines of history stored 790 * b) maintain a physical history file 791 * 792 * It turns out that there is a lot of ghastly hackery here 793 */ 794 795 796 /* 797 * save command in history 798 */ 799 void 800 histsave(lno, cmd, dowrite) 801 int lno; 802 const char *cmd; 803 int dowrite; 804 { 805 register char **hp; 806 char *c, *cp; 807 808 c = str_save(cmd, APERM); 809 if ((cp = strchr(c, '\n')) != NULL) 810 *cp = '\0'; 811 812 if (histfd && dowrite) 813 writehistfile(lno, c); 814 815 hp = histptr; 816 817 if (++hp >= histlist + histsize) { /* remove oldest command */ 818 afree((void*)*histlist, APERM); 819 for (hp = histlist; hp < histlist + histsize - 1; hp++) 820 hp[0] = hp[1]; 821 } 822 *hp = c; 823 histptr = hp; 824 } 825 826 /* 827 * Write history data to a file nominated by HISTFILE 828 * if HISTFILE is unset then history still happens, but 829 * the data is not written to a file 830 * All copies of ksh looking at the file will maintain the 831 * same history. This is ksh behaviour. 832 * 833 * This stuff uses mmap() 834 * if your system ain't got it - then you'll have to undef HISTORYFILE 835 */ 836 837 /* 838 * Open a history file 839 * Format is: 840 * Bytes 1, 2: HMAGIC - just to check that we are dealing with 841 * the correct object 842 * Then follows a number of stored commands 843 * Each command is 844 * <command byte><command number(4 bytes)><bytes><null> 845 */ 846 # define HMAGIC1 0xab 847 # define HMAGIC2 0xcd 848 # define COMMAND 0xff 849 850 void 851 hist_init(s) 852 Source *s; 853 { 854 unsigned char *base; 855 int lines; 856 int fd; 857 858 if (Flag(FTALKING) == 0) 859 return; 860 861 hstarted = 1; 862 863 hist_source = s; 864 865 hname = str_val(global("HISTFILE")); 866 if (hname == NULL) 867 return; 868 hname = str_save(hname, APERM); 869 870 retry: 871 /* we have a file and are interactive */ 872 if ((fd = open(hname, O_RDWR|O_CREAT|O_APPEND, 0600)) < 0) 873 return; 874 875 histfd = savefd(fd, 0); 876 877 (void) flock(histfd, LOCK_EX); 878 879 hsize = lseek(histfd, 0L, SEEK_END); 880 881 if (hsize == 0) { 882 /* add magic */ 883 if (sprinkle(histfd)) { 884 hist_finish(); 885 return; 886 } 887 } 888 else if (hsize > 0) { 889 /* 890 * we have some data 891 */ 892 base = (unsigned char *)mmap(0, hsize, PROT_READ, MAP_FLAGS, histfd, 0); 893 /* 894 * check on its validity 895 */ 896 if (base == MAP_FAILED || *base != HMAGIC1 || base[1] != HMAGIC2) { 897 if (base != MAP_FAILED) 898 munmap((caddr_t)base, hsize); 899 hist_finish(); 900 unlink(hname); 901 goto retry; 902 } 903 if (hsize > 2) { 904 lines = hist_count_lines(base+2, hsize-2); 905 if (lines > histsize) { 906 /* we need to make the file smaller */ 907 if (hist_shrink(base, hsize)) 908 unlink(hname); 909 munmap((caddr_t)base, hsize); 910 hist_finish(); 911 goto retry; 912 } 913 } 914 histload(hist_source, base+2, hsize-2); 915 munmap((caddr_t)base, hsize); 916 } 917 (void) flock(histfd, LOCK_UN); 918 hsize = lseek(histfd, 0L, SEEK_END); 919 } 920 921 typedef enum state { 922 shdr, /* expecting a header */ 923 sline, /* looking for a null byte to end the line */ 924 sn1, /* bytes 1 to 4 of a line no */ 925 sn2, sn3, sn4 926 } State; 927 928 static int 929 hist_count_lines(base, bytes) 930 register unsigned char *base; 931 register int bytes; 932 { 933 State state = shdr; 934 int lines = 0; 935 936 while (bytes--) { 937 switch (state) 938 { 939 case shdr: 940 if (*base == COMMAND) 941 state = sn1; 942 break; 943 case sn1: 944 state = sn2; break; 945 case sn2: 946 state = sn3; break; 947 case sn3: 948 state = sn4; break; 949 case sn4: 950 state = sline; break; 951 case sline: 952 if (*base == '\0') 953 lines++, state = shdr; 954 } 955 base++; 956 } 957 return lines; 958 } 959 960 /* 961 * Shrink the history file to histsize lines 962 */ 963 static int 964 hist_shrink(oldbase, oldbytes) 965 unsigned char *oldbase; 966 int oldbytes; 967 { 968 int fd; 969 char nfile[1024]; 970 struct stat statb; 971 unsigned char *nbase = oldbase; 972 int nbytes = oldbytes; 973 974 nbase = hist_skip_back(nbase, &nbytes, histsize); 975 if (nbase == NULL) 976 return 1; 977 if (nbase == oldbase) 978 return 0; 979 980 /* 981 * create temp file 982 */ 983 (void) shf_snprintf(nfile, sizeof(nfile), "%s.%d", hname, procpid); 984 if ((fd = creat(nfile, 0600)) < 0) 985 return 1; 986 987 if (sprinkle(fd)) { 988 close(fd); 989 unlink(nfile); 990 return 1; 991 } 992 if (write(fd, nbase, nbytes) != nbytes) { 993 close(fd); 994 unlink(nfile); 995 return 1; 996 } 997 /* 998 * worry about who owns this file 999 */ 1000 if (fstat(histfd, &statb) >= 0) 1001 fchown(fd, statb.st_uid, statb.st_gid); 1002 close(fd); 1003 1004 /* 1005 * rename 1006 */ 1007 if (rename(nfile, hname) < 0) 1008 return 1; 1009 return 0; 1010 } 1011 1012 1013 /* 1014 * find a pointer to the data `no' back from the end of the file 1015 * return the pointer and the number of bytes left 1016 */ 1017 static unsigned char * 1018 hist_skip_back(base, bytes, no) 1019 unsigned char *base; 1020 int *bytes; 1021 int no; 1022 { 1023 register int lines = 0; 1024 register unsigned char *ep; 1025 1026 for (ep = base + *bytes; --ep > base; ) { 1027 /* this doesn't really work: the 4 byte line number that is 1028 * encoded after the COMMAND byte can itself contain the 1029 * COMMAND byte.... 1030 */ 1031 for (; ep > base && *ep != COMMAND; ep--) 1032 ; 1033 if (ep == base) 1034 break; 1035 if (++lines == no) { 1036 *bytes = *bytes - ((char *)ep - (char *)base); 1037 return ep; 1038 } 1039 } 1040 return NULL; 1041 } 1042 1043 /* 1044 * load the history structure from the stored data 1045 */ 1046 static void 1047 histload(s, base, bytes) 1048 Source *s; 1049 register unsigned char *base; 1050 register int bytes; 1051 { 1052 State state; 1053 int lno = 0; 1054 unsigned char *line = NULL; 1055 1056 for (state = shdr; bytes-- > 0; base++) { 1057 switch (state) { 1058 case shdr: 1059 if (*base == COMMAND) 1060 state = sn1; 1061 break; 1062 case sn1: 1063 lno = (((*base)&0xff)<<24); 1064 state = sn2; 1065 break; 1066 case sn2: 1067 lno |= (((*base)&0xff)<<16); 1068 state = sn3; 1069 break; 1070 case sn3: 1071 lno |= (((*base)&0xff)<<8); 1072 state = sn4; 1073 break; 1074 case sn4: 1075 lno |= (*base)&0xff; 1076 line = base+1; 1077 state = sline; 1078 break; 1079 case sline: 1080 if (*base == '\0') { 1081 /* worry about line numbers */ 1082 if (histptr >= histlist && lno-1 != s->line) { 1083 /* a replacement ? */ 1084 histinsert(s, lno, line); 1085 } 1086 else { 1087 s->line = lno; 1088 histsave(lno, (char *)line, 0); 1089 } 1090 state = shdr; 1091 } 1092 } 1093 } 1094 } 1095 1096 /* 1097 * Insert a line into the history at a specified number 1098 */ 1099 static void 1100 histinsert(s, lno, line) 1101 Source *s; 1102 int lno; 1103 unsigned char *line; 1104 { 1105 register char **hp; 1106 1107 if (lno >= s->line-(histptr-histlist) && lno <= s->line) { 1108 hp = &histptr[lno-s->line]; 1109 if (*hp) 1110 afree((void*)*hp, APERM); 1111 *hp = str_save((char *)line, APERM); 1112 } 1113 } 1114 1115 /* 1116 * write a command to the end of the history file 1117 * This *MAY* seem easy but it's also necessary to check 1118 * that the history file has not changed in size. 1119 * If it has - then some other shell has written to it 1120 * and we should read those commands to update our history 1121 */ 1122 static void 1123 writehistfile(lno, cmd) 1124 int lno; 1125 char *cmd; 1126 { 1127 int sizenow; 1128 unsigned char *base; 1129 unsigned char *new; 1130 int bytes; 1131 unsigned char hdr[5]; 1132 1133 (void) flock(histfd, LOCK_EX); 1134 sizenow = lseek(histfd, 0L, SEEK_END); 1135 if (sizenow != hsize) { 1136 /* 1137 * Things have changed 1138 */ 1139 if (sizenow > hsize) { 1140 /* someone has added some lines */ 1141 bytes = sizenow - hsize; 1142 base = (unsigned char *)mmap(0, sizenow, PROT_READ, MAP_FLAGS, histfd, 0); 1143 if (base == MAP_FAILED) 1144 goto bad; 1145 new = base + hsize; 1146 if (*new != COMMAND) { 1147 munmap((caddr_t)base, sizenow); 1148 goto bad; 1149 } 1150 hist_source->line--; 1151 histload(hist_source, new, bytes); 1152 hist_source->line++; 1153 lno = hist_source->line; 1154 munmap((caddr_t)base, sizenow); 1155 hsize = sizenow; 1156 } else { 1157 /* it has shrunk */ 1158 /* but to what? */ 1159 /* we'll give up for now */ 1160 goto bad; 1161 } 1162 } 1163 /* 1164 * we can write our bit now 1165 */ 1166 hdr[0] = COMMAND; 1167 hdr[1] = (lno>>24)&0xff; 1168 hdr[2] = (lno>>16)&0xff; 1169 hdr[3] = (lno>>8)&0xff; 1170 hdr[4] = lno&0xff; 1171 (void) write(histfd, hdr, 5); 1172 (void) write(histfd, cmd, strlen(cmd)+1); 1173 hsize = lseek(histfd, 0L, SEEK_END); 1174 (void) flock(histfd, LOCK_UN); 1175 return; 1176 bad: 1177 hist_finish(); 1178 } 1179 1180 void 1181 hist_finish() 1182 { 1183 (void) flock(histfd, LOCK_UN); 1184 (void) close(histfd); 1185 histfd = 0; 1186 } 1187 1188 /* 1189 * add magic to the history file 1190 */ 1191 static int 1192 sprinkle(fd) 1193 int fd; 1194 { 1195 static unsigned char mag[] = { HMAGIC1, HMAGIC2 }; 1196 1197 return(write(fd, mag, 2) != 2); 1198 } 1199 1200 # endif 1201 #else /* HISTORY */ 1202 1203 /* No history to be compiled in: dummy routines to avoid lots more ifdefs */ 1204 void 1205 init_histvec() 1206 { 1207 } 1208 void 1209 hist_init(s) 1210 Source *s; 1211 { 1212 } 1213 void 1214 hist_finish() 1215 { 1216 } 1217 void 1218 histsave(lno, cmd, dowrite) 1219 int lno; 1220 const char *cmd; 1221 int dowrite; 1222 { 1223 errorf("history not enabled"); 1224 } 1225 #endif /* HISTORY */ 1226