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