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