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