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