1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * David Korn <dgk@research.att.com> * 18 * * 19 ***********************************************************************/ 20 #pragma prototyped 21 /* 22 * History file manipulation routines 23 * 24 * David Korn 25 * AT&T Labs 26 * 27 */ 28 29 /* 30 * Each command in the history file starts on an even byte is null terminated. 31 * The first byte must contain the special character HIST_UNDO and the second 32 * byte is the version number. The sequence HIST_UNDO 0, following a command, 33 * nullifies the previous command. A six byte sequence starting with 34 * HIST_CMDNO is used to store the command number so that it is not necessary 35 * to read the file from beginning to end to get to the last block of 36 * commands. This format of this sequence is different in version 1 37 * then in version 0. Version 1 allows commands to use the full 8 bit 38 * character set. It can understand version 0 format files. 39 */ 40 41 42 #define HIST_MAX (sizeof(int)*HIST_BSIZE) 43 #define HIST_BIG (0100000-1024) /* 1K less than maximum short */ 44 #define HIST_LINE 32 /* typical length for history line */ 45 #define HIST_MARKSZ 6 46 #define HIST_RECENT 600 47 #define HIST_UNDO 0201 /* invalidate previous command */ 48 #define HIST_CMDNO 0202 /* next 3 bytes give command number */ 49 #define HIST_BSIZE 4096 /* size of history file buffer */ 50 #define HIST_DFLT 512 /* default size of history list */ 51 52 #define _HIST_PRIVATE \ 53 off_t histcnt; /* offset into history file */\ 54 off_t histmarker; /* offset of last command marker */ \ 55 int histflush; /* set if flushed outside of hflush() */\ 56 int histmask; /* power of two mask for histcnt */ \ 57 char histbuff[HIST_BSIZE+1]; /* history file buffer */ \ 58 int histwfail; \ 59 off_t histcmds[2]; /* offset for recent commands, must be last */ 60 61 #define hist_ind(hp,c) ((int)((c)&(hp)->histmask)) 62 63 #include <ast.h> 64 #include <sfio.h> 65 #include "FEATURE/time" 66 #include <error.h> 67 #include <ctype.h> 68 #include <ls.h> 69 #if KSHELL 70 # include "defs.h" 71 # include "variables.h" 72 # include "path.h" 73 # include "builtins.h" 74 # include "io.h" 75 #endif /* KSHELL */ 76 #include "history.h" 77 78 #if !KSHELL 79 # define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x))) 80 # define NIL(type) ((type)0) 81 # define path_relative(x) (x) 82 # ifdef __STDC__ 83 # define nv_getval(s) getenv(#s) 84 # else 85 # define nv_getval(s) getenv("s") 86 # endif /* __STDC__ */ 87 # define e_unknown "unknown" 88 # define sh_translate(x) (x) 89 char login_sh = 0; 90 char hist_fname[] = "/.history"; 91 #endif /* KSHELL */ 92 93 #ifndef O_BINARY 94 # define O_BINARY 0 95 #endif /* O_BINARY */ 96 97 int _Hist = 0; 98 static void hist_marker(char*,long); 99 static void hist_trim(History_t*, int); 100 static int hist_nearend(History_t*,Sfio_t*, off_t); 101 static int hist_check(int); 102 static int hist_clean(int); 103 #ifdef SF_BUFCONST 104 static ssize_t hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*); 105 static int hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*); 106 #else 107 static int hist_write(Sfio_t*, const void*, int, Sfdisc_t*); 108 static int hist_exceptf(Sfio_t*, int, Sfdisc_t*); 109 #endif 110 111 112 static int histinit; 113 static mode_t histmode; 114 static History_t *wasopen; 115 static History_t *hist_ptr; 116 117 #if SHOPT_ACCTFILE 118 static int acctfd; 119 static char *logname; 120 # include <pwd.h> 121 122 int acctinit(void) 123 { 124 register char *cp, *acctfile; 125 Namval_t *np = nv_search("ACCTFILE",sh.var_tree,0); 126 127 if(!np || !(acctfile=nv_getval(np))) 128 return(0); 129 if(!(cp = getlogin())) 130 { 131 struct passwd *userinfo = getpwuid(getuid()); 132 if(userinfo) 133 cp = userinfo->pw_name; 134 else 135 cp = "unknown"; 136 } 137 logname = strdup(cp); 138 139 if((acctfd=sh_open(acctfile, 140 O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && 141 (unsigned)acctfd < 10) 142 { 143 int n; 144 if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0) 145 { 146 close(acctfd); 147 acctfd = n; 148 } 149 } 150 if(acctfd < 0) 151 { 152 acctfd = 0; 153 return(0); 154 } 155 if(strmatch(acctfile,e_devfdNN)) 156 { 157 char newfile[16]; 158 sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd); 159 nv_putval(np,newfile,NV_RDONLY); 160 } 161 else 162 fcntl(acctfd,F_SETFD,FD_CLOEXEC); 163 return(1); 164 } 165 #endif /* SHOPT_ACCTFILE */ 166 167 static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; 168 static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; 169 170 static void hist_touch(void *handle) 171 { 172 touch((char*)handle, (time_t)0, (time_t)0, 0); 173 } 174 175 /* 176 * open the history file 177 * if HISTNAME is not given and userid==0 then no history file. 178 * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is 179 * cleaned up. 180 * hist_open() returns 1, if history file is open 181 */ 182 int sh_histinit(void) 183 { 184 register int fd; 185 register History_t *hp; 186 register char *histname; 187 char *fname=0; 188 int histmask, maxlines, hist_start=0; 189 register char *cp; 190 register off_t hsize = 0; 191 192 if(sh.hist_ptr=hist_ptr) 193 return(1); 194 if(!(histname = nv_getval(HISTFILE))) 195 { 196 int offset = staktell(); 197 if(cp=nv_getval(HOME)) 198 stakputs(cp); 199 stakputs(hist_fname); 200 stakputc(0); 201 stakseek(offset); 202 histname = stakptr(offset); 203 } 204 #ifdef future 205 if(hp=wasopen) 206 { 207 /* reuse history file if same name */ 208 wasopen = 0; 209 sh.hist_ptr = hist_ptr = hp; 210 if(strcmp(histname,hp->histname)==0) 211 return(1); 212 else 213 hist_free(); 214 } 215 #endif 216 retry: 217 cp = path_relative(histname); 218 if(!histinit) 219 histmode = S_IRUSR|S_IWUSR; 220 if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) 221 { 222 hsize=lseek(fd,(off_t)0,SEEK_END); 223 } 224 if((unsigned)fd <=2) 225 { 226 int n; 227 if((n=fcntl(fd,F_DUPFD,10))>=0) 228 { 229 close(fd); 230 fd=n; 231 } 232 } 233 /* make sure that file has history file format */ 234 if(hsize && hist_check(fd)) 235 { 236 close(fd); 237 hsize = 0; 238 if(unlink(cp)>=0) 239 goto retry; 240 fd = -1; 241 } 242 if(fd < 0) 243 { 244 #if KSHELL 245 /* don't allow root a history_file in /tmp */ 246 if(sh.userid) 247 #endif /* KSHELL */ 248 { 249 if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) 250 return(0); 251 fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); 252 } 253 } 254 if(fd<0) 255 return(0); 256 /* set the file to close-on-exec */ 257 fcntl(fd,F_SETFD,FD_CLOEXEC); 258 if(cp=nv_getval(HISTSIZE)) 259 maxlines = (unsigned)strtol(cp, (char**)0, 10); 260 else 261 maxlines = HIST_DFLT; 262 for(histmask=16;histmask <= maxlines; histmask <<=1 ); 263 if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) 264 { 265 close(fd); 266 return(0); 267 } 268 sh.hist_ptr = hist_ptr = hp; 269 hp->histsize = maxlines; 270 hp->histmask = histmask; 271 hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); 272 memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); 273 hp->histind = 1; 274 hp->histcmds[1] = 2; 275 hp->histcnt = 2; 276 hp->histname = strdup(histname); 277 hp->histdisc = hist_disc; 278 if(hsize==0) 279 { 280 /* put special characters at front of file */ 281 sfwrite(hp->histfp,(char*)hist_stamp,2); 282 sfsync(hp->histfp); 283 } 284 /* initialize history list */ 285 else 286 { 287 int first,last; 288 off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; 289 hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); 290 hist_eof(hp); /* this sets histind to last command */ 291 if((hist_start = (last=(int)hp->histind)-maxlines) <=0) 292 hist_start = 1; 293 mark = hp->histmarker; 294 while(first > hist_start) 295 { 296 size += size; 297 first = hist_nearend(hp,hp->histfp,hsize-size); 298 hp->histind = first; 299 } 300 histinit = hist_start; 301 hist_eof(hp); 302 if(!histinit) 303 { 304 sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); 305 hp->histind = last; 306 hp->histmarker = mark; 307 } 308 histinit = 0; 309 } 310 if(fname) 311 { 312 unlink(fname); 313 free((void*)fname); 314 } 315 if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) 316 { 317 #ifdef DEBUG 318 sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); 319 sfsync(sfstderr); 320 #endif /* DEBUG */ 321 hist_trim(hp,(int)hp->histind-maxlines); 322 } 323 sfdisc(hp->histfp,&hp->histdisc); 324 #if KSHELL 325 (HISTCUR)->nvalue.lp = (&hp->histind); 326 #endif /* KSHELL */ 327 sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); 328 #if SHOPT_ACCTFILE 329 if(sh_isstate(SH_INTERACTIVE)) 330 acctinit(); 331 #endif /* SHOPT_ACCTFILE */ 332 return(1); 333 } 334 335 /* 336 * close the history file and free the space 337 */ 338 339 void hist_close(register History_t *hp) 340 { 341 sfclose(hp->histfp); 342 free((char*)hp); 343 hist_ptr = 0; 344 sh.hist_ptr = 0; 345 #if SHOPT_ACCTFILE 346 if(acctfd) 347 { 348 close(acctfd); 349 acctfd = 0; 350 } 351 #endif /* SHOPT_ACCTFILE */ 352 } 353 354 /* 355 * check history file format to see if it begins with special byte 356 */ 357 static int hist_check(register int fd) 358 { 359 unsigned char magic[2]; 360 lseek(fd,(off_t)0,SEEK_SET); 361 if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO)) 362 return(1); 363 return(0); 364 } 365 366 /* 367 * clean out history file OK if not modified in HIST_RECENT seconds 368 */ 369 static int hist_clean(int fd) 370 { 371 struct stat statb; 372 return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT); 373 } 374 375 /* 376 * Copy the last <n> commands to a new file and make this the history file 377 */ 378 379 static void hist_trim(History_t *hp, int n) 380 { 381 register char *cp; 382 register int incmd=1, c=0; 383 register History_t *hist_new, *hist_old = hp; 384 char *buff, *endbuff, *tmpname=0; 385 off_t oldp,newp; 386 struct stat statb; 387 unlink(hist_old->histname); 388 if(access(hist_old->histname,F_OK) >= 0) 389 { 390 /* The unlink can fail on windows 95 */ 391 int fd; 392 char *last, *name=hist_old->histname; 393 close(sffileno(hist_old->histfp)); 394 tmpname = (char*)malloc(strlen(name)+14); 395 if(last = strrchr(name,'/')) 396 { 397 *last = 0; 398 pathtmp(tmpname,name,"hist",NIL(int*)); 399 *last = '/'; 400 } 401 else 402 pathtmp(tmpname,".","hist",NIL(int*)); 403 if(rename(name,tmpname) < 0) 404 tmpname = name; 405 fd = open(tmpname,O_RDONLY); 406 sfsetfd(hist_old->histfp,fd); 407 if(tmpname==name) 408 tmpname = 0; 409 } 410 hp = hist_ptr = 0; 411 if(fstat(sffileno(hist_old->histfp),&statb)>=0) 412 { 413 histinit = 1; 414 histmode = statb.st_mode; 415 } 416 if(!sh_histinit()) 417 { 418 /* use the old history file */ 419 hist_ptr = hist_old; 420 return; 421 } 422 hist_new = hist_ptr; 423 hist_ptr = hist_old; 424 if(--n < 0) 425 n = 0; 426 newp = hist_seek(hist_old,++n); 427 while(1) 428 { 429 if(!incmd) 430 { 431 c = hist_ind(hist_new,++hist_new->histind); 432 hist_new->histcmds[c] = hist_new->histcnt; 433 if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2) 434 { 435 char locbuff[HIST_MARKSZ]; 436 hist_marker(locbuff,hist_new->histind); 437 sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ); 438 hist_new->histcnt += HIST_MARKSZ; 439 hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt; 440 } 441 oldp = newp; 442 newp = hist_seek(hist_old,++n); 443 if(newp <=oldp) 444 break; 445 } 446 if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0))) 447 break; 448 *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0; 449 /* copy to null byte */ 450 incmd = 0; 451 while(*cp++); 452 if(cp > endbuff) 453 incmd = 1; 454 else if(*cp==0) 455 cp++; 456 if(cp > endbuff) 457 cp = endbuff; 458 c = cp-buff; 459 hist_new->histcnt += c; 460 sfwrite(hist_new->histfp,buff,c); 461 } 462 hist_ptr = hist_new; 463 hist_cancel(hist_ptr); 464 sfclose(hist_old->histfp); 465 if(tmpname) 466 { 467 unlink(tmpname); 468 free(tmpname); 469 } 470 free((char*)hist_old); 471 } 472 473 /* 474 * position history file at size and find next command number 475 */ 476 static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size) 477 { 478 register unsigned char *cp, *endbuff; 479 register int n, incmd=1; 480 unsigned char *buff, marker[4]; 481 if(size <= 2L || sfseek(iop,size,SEEK_SET)<0) 482 goto begin; 483 /* skip to marker command and return the number */ 484 /* numbering commands occur after a null and begin with HIST_CMDNO */ 485 while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR)) 486 { 487 n = sfvalue(iop); 488 *(endbuff=cp+n) = 0; 489 while(1) 490 { 491 /* check for marker */ 492 if(!incmd && *cp++==HIST_CMDNO && *cp==0) 493 { 494 n = cp+1 - buff; 495 incmd = -1; 496 break; 497 } 498 incmd = 0; 499 while(*cp++); 500 if(cp>endbuff) 501 { 502 incmd = 1; 503 break; 504 } 505 if(*cp==0 && ++cp>endbuff) 506 break; 507 } 508 size += n; 509 sfread(iop,(char*)buff,n); 510 if(incmd < 0) 511 { 512 if((n=sfread(iop,(char*)marker,4))==4) 513 { 514 n = (marker[0]<<16)|(marker[1]<<8)|marker[2]; 515 if(n < size/2) 516 { 517 hp->histmarker = hp->histcnt = size+4; 518 return(n); 519 } 520 n=4; 521 } 522 if(n >0) 523 size += n; 524 incmd = 0; 525 } 526 } 527 begin: 528 sfseek(iop,(off_t)2,SEEK_SET); 529 hp->histmarker = hp->histcnt = 2L; 530 return(1); 531 } 532 533 /* 534 * This routine reads the history file from the present position 535 * to the end-of-file and puts the information in the in-core 536 * history table 537 * Note that HIST_CMDNO is only recognized at the beginning of a command 538 * and that HIST_UNDO as the first character of a command is skipped 539 * unless it is followed by 0. If followed by 0 then it cancels 540 * the previous command. 541 */ 542 543 void hist_eof(register History_t *hp) 544 { 545 register char *cp,*first,*endbuff; 546 register int incmd = 0; 547 register off_t count = hp->histcnt; 548 int n,skip=0; 549 sfseek(hp->histfp,count,SEEK_SET); 550 while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0)) 551 { 552 n = sfvalue(hp->histfp); 553 *(endbuff = cp+n) = 0; 554 first = cp += skip; 555 while(1) 556 { 557 while(!incmd) 558 { 559 if(cp>first) 560 { 561 count += (cp-first); 562 n = hist_ind(hp, ++hp->histind); 563 #ifdef future 564 if(count==hp->histcmds[n]) 565 { 566 sfprintf(sfstderr,"count match n=%d\n",n); 567 if(histinit) 568 { 569 histinit = 0; 570 return; 571 } 572 } 573 else if(n>=histinit) 574 #endif 575 hp->histcmds[n] = count; 576 first = cp; 577 } 578 switch(*((unsigned char*)(cp++))) 579 { 580 case HIST_CMDNO: 581 if(*cp==0) 582 { 583 hp->histmarker=count+2; 584 cp += (HIST_MARKSZ-1); 585 hp->histind--; 586 #ifdef future 587 if(cp <= endbuff) 588 { 589 unsigned char *marker = (unsigned char*)(cp-4); 590 int n = ((marker[0]<<16) 591 |(marker[1]<<8)|marker[2]); 592 if((n<count/2) && n != (hp->histind+1)) 593 errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n); 594 } 595 #endif 596 } 597 break; 598 case HIST_UNDO: 599 if(*cp==0) 600 { 601 cp+=1; 602 hp->histind-=2; 603 } 604 break; 605 default: 606 cp--; 607 incmd = 1; 608 } 609 if(cp > endbuff) 610 { 611 cp++; 612 goto refill; 613 } 614 } 615 first = cp; 616 while(*cp++); 617 if(cp > endbuff) 618 break; 619 incmd = 0; 620 while(*cp==0) 621 { 622 if(++cp > endbuff) 623 goto refill; 624 } 625 } 626 refill: 627 count += (--cp-first); 628 skip = (cp-endbuff); 629 if(!incmd && !skip) 630 hp->histcmds[hist_ind(hp,++hp->histind)] = count; 631 } 632 hp->histcnt = count; 633 } 634 635 /* 636 * This routine will cause the previous command to be cancelled 637 */ 638 639 void hist_cancel(register History_t *hp) 640 { 641 register int c; 642 if(!hp) 643 return; 644 sfputc(hp->histfp,HIST_UNDO); 645 sfputc(hp->histfp,0); 646 sfsync(hp->histfp); 647 hp->histcnt += 2; 648 c = hist_ind(hp,--hp->histind); 649 hp->histcmds[c] = hp->histcnt; 650 } 651 652 /* 653 * flush the current history command 654 */ 655 656 void hist_flush(register History_t *hp) 657 { 658 register char *buff; 659 if(hp) 660 { 661 if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR)) 662 { 663 hp->histflush = sfvalue(hp->histfp)+1; 664 sfwrite(hp->histfp,buff,0); 665 } 666 else 667 hp->histflush=0; 668 if(sfsync(hp->histfp)<0) 669 { 670 hist_close(hp); 671 if(!sh_histinit()) 672 sh_offoption(SH_HISTORY); 673 } 674 hp->histflush = 0; 675 } 676 } 677 678 /* 679 * This is the write discipline for the history file 680 * When called from hist_flush(), trailing newlines are deleted and 681 * a zero byte. Line sequencing is added as required 682 */ 683 684 #ifdef SF_BUFCONST 685 static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle) 686 #else 687 static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle) 688 #endif 689 { 690 register History_t *hp = (History_t*)handle; 691 register char *bufptr = ((char*)buff)+insize; 692 register int c,size = insize; 693 register off_t cur; 694 int saved=0; 695 char saveptr[HIST_MARKSZ]; 696 if(!hp->histflush) 697 return(write(sffileno(iop),(char*)buff,size)); 698 if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0) 699 { 700 errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno); 701 return(-1); 702 } 703 hp->histcnt = cur; 704 /* remove whitespace from end of commands */ 705 while(--bufptr >= (char*)buff) 706 { 707 c= *bufptr; 708 if(!isspace(c)) 709 { 710 if(c=='\\' && *(bufptr+1)!='\n') 711 bufptr++; 712 break; 713 } 714 } 715 /* don't count empty lines */ 716 if(++bufptr <= (char*)buff) 717 return(insize); 718 *bufptr++ = '\n'; 719 *bufptr++ = 0; 720 size = bufptr - (char*)buff; 721 #if SHOPT_ACCTFILE 722 if(acctfd) 723 { 724 int timechars, offset; 725 offset = staktell(); 726 stakputs(buff); 727 stakseek(staktell() - 1); 728 timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *))); 729 lseek(acctfd, (off_t)0, SEEK_END); 730 write(acctfd, stakptr(offset), size - 2 + timechars); 731 stakseek(offset); 732 733 } 734 #endif /* SHOPT_ACCTFILE */ 735 if(size&01) 736 { 737 size++; 738 *bufptr++ = 0; 739 } 740 hp->histcnt += size; 741 c = hist_ind(hp,++hp->histind); 742 hp->histcmds[c] = hp->histcnt; 743 if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2) 744 { 745 memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ); 746 saved=1; 747 hp->histcnt += HIST_MARKSZ; 748 hist_marker(bufptr,hp->histind); 749 hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt; 750 size += HIST_MARKSZ; 751 } 752 errno = 0; 753 size = write(sffileno(iop),(char*)buff,size); 754 if(saved) 755 memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ); 756 if(size>=0) 757 { 758 hp->histwfail = 0; 759 return(insize); 760 } 761 return(-1); 762 } 763 764 /* 765 * Put history sequence number <n> into buffer <buff> 766 * The buffer must be large enough to hold HIST_MARKSZ chars 767 */ 768 769 static void hist_marker(register char *buff,register long cmdno) 770 { 771 *buff++ = HIST_CMDNO; 772 *buff++ = 0; 773 *buff++ = (cmdno>>16); 774 *buff++ = (cmdno>>8); 775 *buff++ = cmdno; 776 *buff++ = 0; 777 } 778 779 /* 780 * return byte offset in history file for command <n> 781 */ 782 off_t hist_tell(register History_t *hp, int n) 783 { 784 return(hp->histcmds[hist_ind(hp,n)]); 785 } 786 787 /* 788 * seek to the position of command <n> 789 */ 790 off_t hist_seek(register History_t *hp, int n) 791 { 792 return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET)); 793 } 794 795 /* 796 * write the command starting at offset <offset> onto file <outfile>. 797 * if character <last> appears before newline it is deleted 798 * each new-line character is replaced with string <nl>. 799 */ 800 801 void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl) 802 { 803 register int oldc=0; 804 register int c; 805 if(offset<0 || !hp) 806 { 807 sfputr(outfile,sh_translate(e_unknown),'\n'); 808 return; 809 } 810 sfseek(hp->histfp,offset,SEEK_SET); 811 while((c = sfgetc(hp->histfp)) != EOF) 812 { 813 if(c && oldc=='\n') 814 sfputr(outfile,nl,-1); 815 else if(last && (c==0 || (c=='\n' && oldc==last))) 816 return; 817 else if(oldc) 818 sfputc(outfile,oldc); 819 oldc = c; 820 if(c==0) 821 return; 822 } 823 return; 824 } 825 826 /* 827 * find index for last line with given string 828 * If flag==0 then line must begin with string 829 * direction < 1 for backwards search 830 */ 831 832 Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction) 833 { 834 register int index2; 835 off_t offset; 836 int *coffset=0; 837 Histloc_t location; 838 location.hist_command = -1; 839 location.hist_char = 0; 840 location.hist_line = 0; 841 if(!hp) 842 return(location); 843 /* leading ^ means beginning of line unless escaped */ 844 if(flag) 845 { 846 index2 = *string; 847 if(index2=='\\') 848 string++; 849 else if(index2=='^') 850 { 851 flag=0; 852 string++; 853 } 854 } 855 if(flag) 856 coffset = &location.hist_char; 857 index2 = (int)hp->histind; 858 if(direction<0) 859 { 860 index2 -= hp->histsize; 861 if(index2<1) 862 index2 = 1; 863 if(index1 <= index2) 864 return(location); 865 } 866 else if(index1 >= index2) 867 return(location); 868 while(index1!=index2) 869 { 870 direction>0?++index1:--index1; 871 offset = hist_tell(hp,index1); 872 if((location.hist_line=hist_match(hp,offset,string,coffset))>=0) 873 { 874 location.hist_command = index1; 875 return(location); 876 } 877 #if KSHELL 878 /* allow a search to be aborted */ 879 if(sh.trapnote&SH_SIGSET) 880 break; 881 #endif /* KSHELL */ 882 } 883 return(location); 884 } 885 886 /* 887 * search for <string> in history file starting at location <offset> 888 * If coffset==0 then line must begin with string 889 * returns the line number of the match if successful, otherwise -1 890 */ 891 892 int hist_match(register History_t *hp,off_t offset,char *string,int *coffset) 893 { 894 register unsigned char *first, *cp; 895 register int m,n,c=1,line=0; 896 #if SHOPT_MULTIBYTE 897 mbinit(); 898 #endif /* SHOPT_MULTIBYTE */ 899 sfseek(hp->histfp,offset,SEEK_SET); 900 if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0))) 901 return(-1); 902 m = sfvalue(hp->histfp); 903 n = strlen(string); 904 while(m > n) 905 { 906 if(*cp==*string && memcmp(cp,string,n)==0) 907 { 908 if(coffset) 909 *coffset = (cp-first); 910 return(line); 911 } 912 if(!coffset) 913 break; 914 if(*cp=='\n') 915 line++; 916 #if SHOPT_MULTIBYTE 917 if((c=mbsize(cp)) < 0) 918 c = 1; 919 #endif /* SHOPT_MULTIBYTE */ 920 cp += c; 921 m -= c; 922 } 923 return(-1); 924 } 925 926 927 #if SHOPT_ESH || SHOPT_VSH 928 /* 929 * copy command <command> from history file to s1 930 * at most <size> characters copied 931 * if s1==0 the number of lines for the command is returned 932 * line=linenumber for emacs copy and only this line of command will be copied 933 * line < 0 for full command copy 934 * -1 returned if there is no history file 935 */ 936 937 int hist_copy(char *s1,int size,int command,int line) 938 { 939 register int c; 940 register History_t *hp = sh_getinterp()->hist_ptr; 941 register int count = 0; 942 register char *s1max = s1+size; 943 if(!hp) 944 return(-1); 945 hist_seek(hp,command); 946 while ((c = sfgetc(hp->histfp)) && c!=EOF) 947 { 948 if(c=='\n') 949 { 950 if(count++ ==line) 951 break; 952 else if(line >= 0) 953 continue; 954 } 955 if(s1 && (line<0 || line==count)) 956 { 957 if(s1 >= s1max) 958 { 959 *--s1 = 0; 960 break; 961 } 962 *s1++ = c; 963 } 964 965 } 966 sfseek(hp->histfp,(off_t)0,SEEK_END); 967 if(s1==0) 968 return(count); 969 if(count && (c= *(s1-1)) == '\n') 970 s1--; 971 *s1 = '\0'; 972 return(count); 973 } 974 975 /* 976 * return word number <word> from command number <command> 977 */ 978 979 char *hist_word(char *string,int size,int word) 980 { 981 register int c; 982 register char *s1 = string; 983 register unsigned char *cp = (unsigned char*)s1; 984 register int flag = 0; 985 History_t *hp = hist_ptr; 986 if(!hp) 987 #if KSHELL 988 { 989 strncpy(string,sh.lastarg,size); 990 return(string); 991 } 992 #else 993 return(NIL(char*)); 994 #endif /* KSHELL */ 995 hist_copy(string,size,(int)hp->histind-1,-1); 996 for(;c = *cp;cp++) 997 { 998 c = isspace(c); 999 if(c && flag) 1000 { 1001 *cp = 0; 1002 if(--word==0) 1003 break; 1004 flag = 0; 1005 } 1006 else if(c==0 && flag==0) 1007 { 1008 s1 = (char*)cp; 1009 flag++; 1010 } 1011 } 1012 *cp = 0; 1013 if(s1 != string) 1014 strcpy(string,s1); 1015 return(string); 1016 } 1017 1018 #endif /* SHOPT_ESH */ 1019 1020 #if SHOPT_ESH 1021 /* 1022 * given the current command and line number, 1023 * and number of lines back or foward, 1024 * compute the new command and line number. 1025 */ 1026 1027 Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines) 1028 { 1029 Histloc_t next; 1030 line += lines; 1031 if(!hp) 1032 { 1033 command = -1; 1034 goto done; 1035 } 1036 if(lines > 0) 1037 { 1038 register int count; 1039 while(command <= hp->histind) 1040 { 1041 count = hist_copy(NIL(char*),0, command,-1); 1042 if(count > line) 1043 goto done; 1044 line -= count; 1045 command++; 1046 } 1047 } 1048 else 1049 { 1050 register int least = (int)hp->histind-hp->histsize; 1051 while(1) 1052 { 1053 if(line >=0) 1054 goto done; 1055 if(--command < least) 1056 break; 1057 line += hist_copy(NIL(char*),0, command,-1); 1058 } 1059 command = -1; 1060 } 1061 done: 1062 next.hist_line = line; 1063 next.hist_command = command; 1064 return(next); 1065 } 1066 #endif /* SHOPT_ESH */ 1067 1068 1069 /* 1070 * Handle history file exceptions 1071 */ 1072 #ifdef SF_BUFCONST 1073 static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle) 1074 #else 1075 static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle) 1076 #endif 1077 { 1078 register int newfd,oldfd; 1079 History_t *hp = (History_t*)handle; 1080 if(type==SF_WRITE) 1081 { 1082 if(errno==ENOSPC || hp->histwfail++ >= 10) 1083 return(0); 1084 /* write failure could be NFS problem, try to re-open */ 1085 close(oldfd=sffileno(fp)); 1086 if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0) 1087 { 1088 if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd) 1089 return(-1); 1090 fcntl(oldfd,F_SETFD,FD_CLOEXEC); 1091 close(newfd); 1092 if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt) 1093 { 1094 register int index = hp->histind; 1095 lseek(oldfd,(off_t)2,SEEK_SET); 1096 hp->histcnt = 2; 1097 hp->histind = 1; 1098 hp->histcmds[1] = 2; 1099 hist_eof(hp); 1100 hp->histmarker = hp->histcnt; 1101 hp->histind = index; 1102 } 1103 return(1); 1104 } 1105 errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname); 1106 return(-1); 1107 } 1108 return(0); 1109 } 1110