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