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