1 /* 2 3 * Copyright (c) 1984, 1985, 1986 AT&T 4 * All Rights Reserved 5 6 * THIS IS UNPUBLISHED PROPRIETARY SOURCE 7 * CODE OF AT&T. 8 * The copyright notice above does not 9 * evidence any actual or intended 10 * publication of such source code. 11 12 */ 13 /* @(#)history.c 1.1 */ 14 15 /* 16 * History file manipulation routines 17 * 18 * David Korn 19 * AT&T Bell Laboratories 20 * Room 5D-112 21 * Murray Hill, N. J. 07974 22 * Tel. x7975 23 * 24 */ 25 26 27 /* 28 * Each command in the history file starts on an even byte is null terminated. 29 * The first byte must contain the special character H_UNDO and the second 30 * byte is the version number. The sequence H_UNDO 0, following a command, 31 * nullifies the previous command. A six byte sequence starting with 32 * H_CMDNO is used to store the command number so that it is not necessary 33 * to read the file from beginning to end to get to the last block of 34 * commands. This format of this sequence is different in version 1 35 * then in version 0. Version 1 allows commands to use the full 8 bit 36 * character set. It can understand version 0 format files. 37 */ 38 39 40 #ifdef KSHELL 41 #include "defs.h" 42 #include "io.h" 43 #include "flags.h" 44 #include "name.h" 45 #include "shtype.h" 46 #include "stak.h" 47 #include "brkincr.h" 48 #include "builtins.h" 49 #else 50 #include <stdio.h> 51 #include <setjmp.h> 52 #include <signal.h> 53 #include <ctype.h> 54 #endif /* KSHELL */ 55 56 #include "history.h" 57 #ifdef MULTIBYTE 58 #include "national.h" 59 #endif /* MULTIBYTE */ 60 61 int hist_open(); 62 void hist_close(); 63 long hist_list(); 64 void hist_flush(); 65 void hist_cancel(); 66 void hist_eof(); 67 histloc hist_find(); 68 #ifdef ESH 69 histloc hist_locate(); 70 #endif /* ESH */ 71 long hist_position(); 72 #ifdef KSHELL 73 void hist_subst(); 74 #endif /* KSHELL */ 75 76 #ifdef KSHELL 77 extern char *valup(); 78 extern long aeval(); 79 extern FILE *chkrdwr(); 80 extern FILE *create(); 81 extern void failed(); 82 extern void p_str(); 83 #else 84 #define frenumber hist_rename 85 #define tmp_open(s) tmpfile() 86 #define p_str(s,c) (fputs(s,stderr),putc(c,stderr)) 87 #define closefd(f) fclose(f) 88 #define aeval(str) atoi(str) 89 #define TMPSIZ 20 90 #define NL '\n' 91 #define output stderr 92 struct fixcmd *fc_fix; 93 extern char *getenv(); 94 extern FILE *hist_rename(); 95 char login_sh = 0; 96 MSG histfname = "./history"; 97 #define unknown "unknown" 98 #endif /* KSHELL */ 99 extern char *substitute(); 100 extern FILE *tmp_open(); 101 extern long lseek(); 102 extern char *malloc(); 103 extern char *movstr(); 104 extern void free(); 105 106 static int fixmask; 107 static void hist_trim(); 108 static int hist_nearend(); 109 static int hist_check(); 110 static int hist_version; 111 static int heof; 112 113 114 /* 115 * open the history file 116 * if HISTNAME is not given and userid==0 then no history file. 117 * if login_sh and HISTFILE is longer than HISTMAX bytes then it is 118 * cleaned up. 119 */ 120 int hist_open() 121 { 122 register FILE *fd; 123 register struct fixcmd *fp; 124 register char *histname; 125 char fname[TMPSIZ]; 126 char hname[256]; 127 int maxlines; 128 register char *cp; 129 register long hsize = 0; 130 int his_start; 131 132 if(fc_fix) 133 return(0); 134 histname = valup(HISTFILE); 135 if(histname==NULL) 136 { 137 #ifdef KSHELL 138 if(userid==0 && login_sh) 139 return(-1); 140 #endif /* KSHELL */ 141 cp = movstr(valup(HOME),hname); 142 movstr(histfname,cp); 143 histname = hname; 144 } 145 *fname = 0; 146 retry: 147 /* first try to open the current file */ 148 #ifdef KSHELL 149 if((fd=fdopen(open(histname,012),"a+"))==NULL) 150 { 151 /* if you can't then try to create it */ 152 if(fd=create(histname)) 153 { 154 fd = chkrdwr(histname,fd); 155 chmod(histname,0600); 156 } 157 } 158 else 159 hsize=lseek(fileno(fd),0L,2); 160 #else 161 if(fd=fopen(histname,"a+")) 162 { 163 chmod(histname,0600); 164 hsize=lseek(fileno(fd),0L,2); 165 } 166 #endif /* KSHELL */ 167 /* make sure that file has history file format */ 168 if(hsize && hist_check(fd)) 169 { 170 fclose(fd); 171 unlink(histname); 172 hsize = 0; 173 goto retry; 174 } 175 if(fd == NULL) 176 fd = tmp_open(fname); 177 if(fd==NULL) 178 return(-1); 179 fd = frenumber(fd,FCIO); 180 if(cp=valup(HISTSIZE)) 181 maxlines = (unsigned)aeval(cp); 182 else 183 maxlines = HIS_DFLT; 184 for(fixmask=16;fixmask <= maxlines; fixmask <<=1 ); 185 if((fp=(struct fixcmd*)malloc(sizeof(struct fixcmd)+ (--fixmask)*sizeof(long)))==NULL) 186 { 187 fclose(fd); 188 return(-1); 189 } 190 fc_fix = fp; 191 fp->fixfd = fd; 192 fp->fixmax = maxlines; 193 setbuf(fd,malloc(BUFSIZ)); 194 fp->fixind = 1; 195 fp->fixline = 0; 196 fp->fixcmds[1] = 2; 197 fp->fixcnt = 2; 198 if(hsize==0) 199 { 200 /* put special characters at front of file */ 201 putc(H_UNDO,fd); 202 putc(H_VERSION,fd); 203 fflush(fd); 204 } 205 /* initialize history list */ 206 if(hsize) 207 { 208 int nlines = maxlines; 209 long size = hsize - (HISMAX/4); 210 do 211 { 212 size -= ((HISMAX/4) + nlines*HISLINE); 213 his_start = fp->fixind = hist_nearend(fd,size); 214 hist_eof(); 215 nlines = maxlines - (fp->fixind-his_start); 216 } 217 while(his_start >1 && nlines>0); 218 } 219 if(*fname) 220 unlink(fname); 221 if(login_sh && his_start>1 && hsize > HISMAX) 222 { 223 FILE *fdo; 224 if((fdo=fdopen(open(histname,0),"r"))==NULL) 225 return(0); 226 unlink(histname); 227 hist_trim(fdo,fp->fixind-maxlines); 228 } 229 return(0); 230 } 231 232 /* 233 * check history file format to see if it begins with special byte 234 */ 235 236 static int hist_check(fd) 237 register FILE *fd; 238 { 239 setbuf(fd,NULL); 240 fseek(fd,0L,0); 241 if(getc(fd) != H_UNDO) 242 return(1); 243 hist_version = getc(fd); 244 return(0); 245 } 246 247 /* 248 * Copy the last <n> commands to a new file and make this the history file 249 */ 250 251 static void hist_trim(fdo,n) 252 register FILE *fdo; 253 { 254 register FILE *fd; 255 register int c; 256 register struct fixcmd *fp = fc_fix; 257 struct fixcmd *fsave; 258 /* use the old history I/O buffer for fdo */ 259 setbuf(fdo,fp->fixfd->_base); 260 setbuf(fp->fixfd,NULL); 261 fc_fix = 0; 262 hist_open(); 263 if(fc_fix==0) 264 return; 265 fsave = fc_fix; 266 fd = fc_fix->fixfd; 267 do 268 { 269 fc_fix = fp; 270 fseek(fdo,hist_position(++n),0); 271 fc_fix = fsave; 272 while((c=getc(fdo))!=EOF && c) 273 { 274 putc(c,fd); 275 } 276 #ifdef KSHELL 277 states |= FIXFLG; 278 #endif /* KSHELL */ 279 hist_flush(); 280 } 281 while(c!=EOF); 282 fclose(fdo); 283 free((char*)fdo->_base); 284 free((char*)fp); 285 } 286 287 /* 288 * position history file at size and find next command number 289 */ 290 291 static int hist_nearend(fd,size) 292 register FILE *fd; 293 long size; 294 { 295 register int n = 0; 296 register int state = -1; 297 register int c; 298 if(size <=0) 299 goto begin; 300 fseek(fd,size,0); 301 /* skip to numbered command and return the number */ 302 /* numbering commands occur after a null and begin with H_CMDNO */ 303 while((c=getc(fd))!=EOF) 304 { 305 if(state==5) 306 { 307 return(n); 308 } 309 else if(state>0) 310 { 311 if(state==1) 312 { 313 /* see if H_CMDNO is followed by 0 */ 314 if(hist_version && c) 315 { 316 n += 2; 317 state = -1; 318 continue; 319 } 320 n = 0; 321 } 322 if(hist_version) 323 n = (n<<8) + c; 324 else if(state < 3) 325 n = (n<<7) + (c&0177); 326 state++; 327 } 328 else if(state==0 && c==H_CMDNO) 329 { 330 fc_fix->fixcnt = size + n + 6; 331 state = 1; 332 } 333 else 334 { 335 state = (c==0?0:-1); 336 n++; 337 } 338 } 339 begin: 340 fseek(fd,2L,0); 341 fc_fix->fixcnt = 2; 342 return(1); 343 } 344 345 /* 346 * This routine unlinks the history file if the file is a temp file 347 */ 348 349 void hist_close() 350 { 351 if(fc_fix) 352 fclose(fc_fix->fixfd); 353 } 354 355 /* 356 * This routine reads the history file from the present position 357 * to the end-of-file and puts the information in the in-core 358 * history table 359 * Note that H_CMDNO is only recognized at the beginning of a command 360 * and that H_UNDO as the first character of a command is skipped 361 * unless it is followed by 0. If followed by 0 then it cancels 362 * the previous command. 363 */ 364 365 void hist_eof() 366 { 367 register struct fixcmd *fp = fc_fix; 368 register int c; 369 register int incr = 0; 370 register int oldc = 0; 371 register long count = fp->fixcnt; 372 int skip = 0; 373 heof++; /* don't add line number markers */ 374 fseek(fp->fixfd,count,0); 375 while((c=getc(fp->fixfd))!=EOF) 376 { 377 count++; 378 if(skip-- > 0) 379 { 380 oldc = 0; 381 continue; 382 } 383 if(c == 0) 384 { 385 if(oldc==H_CMDNO && incr==0) 386 skip = 3; 387 fp->fixind += incr; 388 fp->fixcmds[fp->fixind&fixmask] = count; 389 incr = 0; 390 } 391 else if(oldc == 0) 392 { 393 if(c == H_CMDNO) 394 { 395 /* old format history file */ 396 if(hist_version==0) 397 skip = 4; 398 incr = 0; 399 } 400 else if(c==H_UNDO) 401 incr = -1; 402 } 403 else 404 incr = 1; 405 oldc = c; 406 } 407 fp->fixline = 0; 408 fp->fixcnt = count; 409 heof = 0; 410 } 411 412 /* 413 * This routine will cause the previous command to be cancelled 414 */ 415 416 void hist_cancel() 417 { 418 register struct fixcmd *fp = fc_fix; 419 register FILE *fd; 420 register int c; 421 if(fp==NULL) 422 return; 423 fd = fp->fixfd; 424 putc(H_UNDO,fd); 425 putc(0,fd); 426 fflush(fd); 427 fp->fixcnt += 2; 428 c = (--fp->fixind)&fixmask; 429 fp->fixcmds[c] = fp->fixcnt; 430 } 431 432 /* 433 * This routine adds one or two null bytes and flushes the history buffer 434 */ 435 436 void hist_flush() 437 { 438 register struct fixcmd *fp = fc_fix; 439 register FILE *fd; 440 register int c; 441 if(fp==NULL) 442 return; 443 #ifdef KSHELL 444 if((states&FIXFLG) == 0) 445 return; 446 states &= ~FIXFLG; 447 #endif /* KSHELL */ 448 fd = fp->fixfd; 449 fp->fixline = 0; 450 /* remove whitespace from end of commands */ 451 while(--fd->_ptr >= fd->_base) 452 { 453 if((c= *fd->_ptr)!=NL && !isspace(c)) 454 break; 455 } 456 fd->_cnt = ++fd->_ptr - fd->_base; 457 if(fd->_cnt<=0) 458 { 459 fp->fixind--; 460 goto set_count; 461 } 462 putc(NL,fd); 463 putc('\0',fd); 464 fflush(fd); 465 set_count: 466 fp->fixcnt = lseek(fileno(fd),0L,2); 467 /* start each command on an even byte boundary */ 468 if(fp->fixcnt&01) 469 { 470 fp->fixcnt++; 471 putc('\0',fd); 472 fflush(fd); 473 } 474 c = (++fp->fixind)&fixmask; 475 fp->fixcmds[c] = fp->fixcnt; 476 if((c = fp->fixcmds[c])> (HISMAX/4) && !heof) 477 { 478 /* put line number in file */ 479 fp->fixcnt += 6; 480 putc(H_CMDNO,fd); 481 putc(0,fd); 482 c = (fp->fixind>>16); 483 putc(c,fd); 484 c = (fp->fixind>>8); 485 putc(c,fd); 486 c = fp->fixind; 487 putc(c,fd); 488 putc(0,fd); 489 fflush(fd); 490 fp->fixcmds[c&fixmask] = fp->fixcnt; 491 } 492 } 493 494 /* 495 * return byte offset in history file for command <n> 496 */ 497 498 long hist_position(n) 499 int n; 500 { 501 register struct fixcmd *fp = fc_fix; 502 return(fp->fixcmds[n&fixmask]); 503 } 504 505 /* 506 * write the command starting at offset <offset> onto file <fd>. 507 * listing stops when character <last> is encountered or end-of-string. 508 * each new-line character is replaced with string <nl>. 509 */ 510 511 long hist_list(offset,last,nl) 512 long offset; 513 int last; 514 char *nl; 515 { 516 register int oldc; 517 register FILE *fd; 518 register int c; 519 if(offset<0) 520 { 521 p_str(unknown,NL); 522 return(-1); 523 } 524 fd = fc_fix->fixfd; 525 fseek(fd,offset,0); 526 oldc=getc(fd); 527 for(offset++;oldc && oldc!=last;oldc=c,offset++) 528 { 529 if((c = getc(fd)) == EOF) 530 return(offset); 531 if(oldc=='\n') 532 { 533 if(c) 534 { 535 fputs(nl,output); 536 continue; 537 } 538 /* don't print trailing newline for job control */ 539 else if(last=='&') 540 return(offset); 541 } 542 putc(oldc,output); 543 } 544 return(offset); 545 } 546 547 /* 548 * find index for last line with given string 549 * If flag==0 then line must begin with string 550 * direction < 1 for backwards search 551 */ 552 553 histloc hist_find(string,index1,flag,direction) 554 char *string; 555 int index1; 556 int direction; 557 { 558 register struct fixcmd *fp = fc_fix; 559 register char *cp; 560 register int c; 561 long offset; 562 int count; 563 int index2; 564 histloc location; 565 #ifdef MULTIBYTE 566 int nbytes = 0; 567 #endif /* MULTIBYTE */ 568 location.his_command = -1; 569 if(fp==NULL) 570 return(location); 571 index2 = fp->fixind; 572 if(direction<0) 573 { 574 index2 -= fp->fixmax; 575 if(index2<1) 576 index2 = 1; 577 if(index1 <= index2) 578 return(location); 579 } 580 else if(index1 >= index2) 581 return(location); 582 while(index1!=index2) 583 { 584 direction>0?++index1:--index1; 585 offset = hist_position(index1); 586 location.his_line = 0; 587 #ifdef KSHELL 588 /* allow a search to be aborted */ 589 if(trapnote&SIGSET) 590 exitsh(SIGFAIL); 591 #endif /* KSHELL */ 592 do 593 { 594 if(offset>=0) 595 { 596 fseek(fp->fixfd,offset,0); 597 count = offset; 598 } 599 offset = -1; 600 for(cp=string;*cp;cp++) 601 { 602 if((c=getc(fp->fixfd)) == EOF) 603 break; 604 if(c == 0) 605 break; 606 count++; 607 #ifdef MULTIBYTE 608 /* always position at character boundary */ 609 if(--nbytes > 0) 610 { 611 if(cp==string) 612 { 613 cp--; 614 continue; 615 } 616 } 617 else 618 { 619 nbytes = echarset(c); 620 nbytes = in_csize(nbytes) + (nbytes>=2); 621 } 622 #endif /* MULTIBYTE */ 623 if(c == '\n') 624 location.his_line++; 625 /* save earliest possible matching character */ 626 if(flag && c == *string && offset<0) 627 offset = count; 628 if(*cp != c ) 629 break; 630 } 631 if(*cp==0) 632 /* match found */ 633 { 634 location.his_command = index1; 635 return(location); 636 } 637 } 638 while(flag && c && c != EOF); 639 } 640 fseek(fp->fixfd,0L,2); 641 return(location); 642 } 643 644 #if ESH || VSH 645 /* 646 * copy command <command> from history file to s1 647 * at most MAXLINE characters copied 648 * if s1==0 the number of lines for the command is returned 649 * line=linenumber for emacs copy and only this line of command will be copied 650 * line < 0 for full command copy 651 * -1 returned if there is no history file 652 */ 653 654 int hist_copy(s1,command,line) 655 register char *s1; 656 { 657 register int c; 658 register struct fixcmd *fp = fc_fix; 659 register int count = 0; 660 register char *s1max = s1+MAXLINE; 661 long offset; 662 if(fp==NULL) 663 return(-1); 664 offset = hist_position(command); 665 fseek(fp->fixfd,offset,0); 666 while ((c = getc(fp->fixfd)) && c!=EOF) 667 { 668 if(c=='\n') 669 { 670 if(count++ ==line) 671 break; 672 else if(line >= 0) 673 continue; 674 } 675 if(s1 && (line<0 || line==count)) 676 { 677 if(s1 >= s1max) 678 { 679 *--s1 = 0; 680 break; 681 } 682 *s1++ = c; 683 } 684 685 } 686 if(s1==0) 687 return(count); 688 if((c= *(s1-1)) == '\n') 689 s1--; 690 *s1 = '\0'; 691 fseek(fp->fixfd,0L,2); 692 return(count); 693 } 694 695 /* 696 * return word number <word> from command number <command> 697 */ 698 699 char *hist_word(s1,word) 700 char *s1; 701 { 702 register int c; 703 register char *cp = s1; 704 register int flag = 0; 705 if(fc_fix==0) 706 #ifdef KSHELL 707 return(lastarg); 708 #else 709 return(NULL); 710 #endif /* KSHELL */ 711 hist_copy(s1,fc_fix->fixind-1,-1); 712 for(;c = *cp;cp++) 713 { 714 c = isspace(c); 715 if(c && flag) 716 { 717 *cp = 0; 718 if(--word==0) 719 break; 720 flag = 0; 721 } 722 else if(c==0 && flag==0) 723 { 724 s1 = cp; 725 flag++; 726 } 727 } 728 *cp = 0; 729 return(s1); 730 } 731 732 #endif /* ESH */ 733 734 #ifdef ESH 735 /* 736 * given the current command and line number, 737 * and number of lines back or foward, 738 * compute the new command and line number. 739 */ 740 741 histloc hist_locate(command,line,lines) 742 register int command; 743 register int line; 744 int lines; 745 { 746 histloc next; 747 line += lines; 748 if(fc_fix==NULL) 749 { 750 command = -1; 751 goto done; 752 } 753 if(lines > 0) 754 { 755 register int count; 756 while(command <= fc_fix->fixind) 757 { 758 count = hist_copy(NIL,command,-1); 759 if(count > line) 760 goto done; 761 line -= count; 762 command++; 763 } 764 } 765 else 766 { 767 register int least = fc_fix->fixind-fc_fix->fixmax; 768 while(1) 769 { 770 if(line >=0) 771 goto done; 772 if(--command < least) 773 break; 774 line += hist_copy(NIL,command,-1); 775 } 776 command = -1; 777 } 778 next.his_command = command; 779 return(next); 780 done: 781 next.his_line = line; 782 next.his_command = command; 783 return(next); 784 } 785 #endif /* ESH */ 786 787 #ifdef KSHELL 788 789 /* 790 * given a file containing a command and a string of the form old=new, 791 * execute the command with the string old replaced by new 792 */ 793 void hist_subst(command,fd,replace) 794 char *command; 795 FILE *fd; 796 char *replace; 797 { 798 register char *new=replace; 799 register char *sp = locstak(); 800 register int c; 801 char *string; 802 while(*++new != '='); /* skip to '=' */ 803 while ((c=getc(fd)) != EOF) 804 *sp++ = c; 805 string = endstak(sp); 806 fclose(fd); 807 *new++ = 0; 808 if(substitute(string,replace,new,(sp=locstak()))) 809 endstak(sp+strlen(sp)); 810 else 811 failed(command,badsub); 812 *(new-1) = '='; 813 fputs(sp,fc_fix->fixfd); 814 hist_flush(); 815 fputs(sp,output); 816 execexp(sp,(FILE*)0); 817 } 818 #endif /* KSHELL */ 819