1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1982-2011 AT&T Intellectual Property * 5* and is licensed under the * 6* Common Public License, Version 1.0 * 7* by AT&T Intellectual Property * 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#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 108int _Hist = 0; 109static void hist_marker(char*,long); 110static History_t* hist_trim(History_t*, int); 111static int hist_nearend(History_t*,Sfio_t*, off_t); 112static int hist_check(int); 113static 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 123static int histinit; 124static mode_t histmode; 125static History_t *wasopen; 126static 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 178static int sh_checkaudit(History_t *hp, const char *name, char *logbuf, size_t len) 179{ 180 char *buff, *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==' '); 205done: 206 close(fd); 207 return(r); 208 209} 210#endif /*SHOPT_AUDIT*/ 211 212static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; 213static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; 214 215static 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 */ 227int 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 262retry: 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 hist_eof(hp); /* this sets histind to last command */ 338 if((hist_start = (last=(int)hp->histind)-maxlines) <=0) 339 hist_start = 1; 340 mark = hp->histmarker; 341 while(first > hist_start) 342 { 343 size += size; 344 first = hist_nearend(hp,hp->histfp,hsize-size); 345 hp->histind = first; 346 } 347 histinit = hist_start; 348 hist_eof(hp); 349 if(!histinit) 350 { 351 sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); 352 hp->histind = last; 353 hp->histmarker = mark; 354 } 355 histinit = 0; 356 } 357 if(fname) 358 { 359 unlink(fname); 360 free((void*)fname); 361 } 362 if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) 363 { 364#ifdef DEBUG 365 sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); 366 sfsync(sfstderr); 367#endif /* DEBUG */ 368 hp = hist_trim(hp,(int)hp->histind-maxlines); 369 } 370 sfdisc(hp->histfp,&hp->histdisc); 371#if KSHELL 372 (HISTCUR)->nvalue.lp = (&hp->histind); 373#endif /* KSHELL */ 374 sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); 375#if SHOPT_ACCTFILE 376 if(sh_isstate(SH_INTERACTIVE)) 377 acctinit(hp); 378#endif /* SHOPT_ACCTFILE */ 379#if SHOPT_AUDIT 380 { 381 char buff[SF_BUFSIZE]; 382 hp->auditfp = 0; 383 if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) 384 { 385 if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) 386 { 387 int n; 388 if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) 389 { 390 sh_close(fd); 391 fd = n; 392 } 393 } 394 if(fd>=0) 395 { 396 fcntl(fd,F_SETFD,FD_CLOEXEC); 397 hp->tty = strdup(ttyname(2)); 398 hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); 399 } 400 } 401 } 402#endif 403 return(1); 404} 405 406/* 407 * close the history file and free the space 408 */ 409 410void hist_close(register History_t *hp) 411{ 412 sfclose(hp->histfp); 413#if SHOPT_AUDIT 414 if(hp->auditfp) 415 { 416 if(hp->tty) 417 free((void*)hp->tty); 418 sfclose(hp->auditfp); 419 } 420#endif /* SHOPT_AUDIT */ 421 free((char*)hp); 422 hist_ptr = 0; 423 shgd->hist_ptr = 0; 424#if SHOPT_ACCTFILE 425 if(acctfd) 426 { 427 close(acctfd); 428 acctfd = 0; 429 } 430#endif /* SHOPT_ACCTFILE */ 431} 432 433/* 434 * check history file format to see if it begins with special byte 435 */ 436static int hist_check(register int fd) 437{ 438 unsigned char magic[2]; 439 lseek(fd,(off_t)0,SEEK_SET); 440 if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO)) 441 return(1); 442 return(0); 443} 444 445/* 446 * clean out history file OK if not modified in HIST_RECENT seconds 447 */ 448static int hist_clean(int fd) 449{ 450 struct stat statb; 451 return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT); 452} 453 454/* 455 * Copy the last <n> commands to a new file and make this the history file 456 */ 457 458static History_t* hist_trim(History_t *hp, int n) 459{ 460 register char *cp; 461 register int incmd=1, c=0; 462 register History_t *hist_new, *hist_old = hp; 463 char *buff, *endbuff, *tmpname=0; 464 off_t oldp,newp; 465 struct stat statb; 466 unlink(hist_old->histname); 467 if(access(hist_old->histname,F_OK) >= 0) 468 { 469 /* The unlink can fail on windows 95 */ 470 int fd; 471 char *last, *name=hist_old->histname; 472 close(sffileno(hist_old->histfp)); 473 tmpname = (char*)malloc(strlen(name)+14); 474 if(last = strrchr(name,'/')) 475 { 476 *last = 0; 477 pathtmp(tmpname,name,"hist",NIL(int*)); 478 *last = '/'; 479 } 480 else 481 pathtmp(tmpname,".","hist",NIL(int*)); 482 if(rename(name,tmpname) < 0) 483 tmpname = name; 484 fd = open(tmpname,O_RDONLY); 485 sfsetfd(hist_old->histfp,fd); 486 if(tmpname==name) 487 tmpname = 0; 488 } 489 hist_ptr = 0; 490 if(fstat(sffileno(hist_old->histfp),&statb)>=0) 491 { 492 histinit = 1; 493 histmode = statb.st_mode; 494 } 495 if(!sh_histinit(hp->histshell)) 496 { 497 /* use the old history file */ 498 return hist_ptr = hist_old; 499 } 500 hist_new = hist_ptr; 501 hist_ptr = hist_old; 502 if(--n < 0) 503 n = 0; 504 newp = hist_seek(hist_old,++n); 505 while(1) 506 { 507 if(!incmd) 508 { 509 c = hist_ind(hist_new,++hist_new->histind); 510 hist_new->histcmds[c] = hist_new->histcnt; 511 if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2) 512 { 513 char locbuff[HIST_MARKSZ]; 514 hist_marker(locbuff,hist_new->histind); 515 sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ); 516 hist_new->histcnt += HIST_MARKSZ; 517 hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt; 518 } 519 oldp = newp; 520 newp = hist_seek(hist_old,++n); 521 if(newp <=oldp) 522 break; 523 } 524 if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0))) 525 break; 526 *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0; 527 /* copy to null byte */ 528 incmd = 0; 529 while(*cp++); 530 if(cp > endbuff) 531 incmd = 1; 532 else if(*cp==0) 533 cp++; 534 if(cp > endbuff) 535 cp = endbuff; 536 c = cp-buff; 537 hist_new->histcnt += c; 538 sfwrite(hist_new->histfp,buff,c); 539 } 540 hist_cancel(hist_new); 541 sfclose(hist_old->histfp); 542 if(tmpname) 543 { 544 unlink(tmpname); 545 free(tmpname); 546 } 547 free((char*)hist_old); 548 return hist_ptr = hist_new; 549} 550 551/* 552 * position history file at size and find next command number 553 */ 554static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size) 555{ 556 register unsigned char *cp, *endbuff; 557 register int n, incmd=1; 558 unsigned char *buff, marker[4]; 559 if(size <= 2L || sfseek(iop,size,SEEK_SET)<0) 560 goto begin; 561 /* skip to marker command and return the number */ 562 /* numbering commands occur after a null and begin with HIST_CMDNO */ 563 while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR)) 564 { 565 n = sfvalue(iop); 566 *(endbuff=cp+n) = 0; 567 while(1) 568 { 569 /* check for marker */ 570 if(!incmd && *cp++==HIST_CMDNO && *cp==0) 571 { 572 n = cp+1 - buff; 573 incmd = -1; 574 break; 575 } 576 incmd = 0; 577 while(*cp++); 578 if(cp>endbuff) 579 { 580 incmd = 1; 581 break; 582 } 583 if(*cp==0 && ++cp>endbuff) 584 break; 585 } 586 size += n; 587 sfread(iop,(char*)buff,n); 588 if(incmd < 0) 589 { 590 if((n=sfread(iop,(char*)marker,4))==4) 591 { 592 n = (marker[0]<<16)|(marker[1]<<8)|marker[2]; 593 if(n < size/2) 594 { 595 hp->histmarker = hp->histcnt = size+4; 596 return(n); 597 } 598 n=4; 599 } 600 if(n >0) 601 size += n; 602 incmd = 0; 603 } 604 } 605begin: 606 sfseek(iop,(off_t)2,SEEK_SET); 607 hp->histmarker = hp->histcnt = 2L; 608 return(1); 609} 610 611/* 612 * This routine reads the history file from the present position 613 * to the end-of-file and puts the information in the in-core 614 * history table 615 * Note that HIST_CMDNO is only recognized at the beginning of a command 616 * and that HIST_UNDO as the first character of a command is skipped 617 * unless it is followed by 0. If followed by 0 then it cancels 618 * the previous command. 619 */ 620 621void hist_eof(register History_t *hp) 622{ 623 register char *cp,*first,*endbuff; 624 register int incmd = 0; 625 register off_t count = hp->histcnt; 626 int oldind,n,skip=0; 627 off_t last = sfseek(hp->histfp,(off_t)0,SEEK_END); 628 if(last < count) 629 { 630 last = -1; 631 count = 2+HIST_MARKSZ; 632 oldind = hp->histind; 633 if((hp->histind -= hp->histsize) < 0) 634 hp->histind = 1; 635 } 636again: 637 sfseek(hp->histfp,count,SEEK_SET); 638 while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0)) 639 { 640 n = sfvalue(hp->histfp); 641 *(endbuff = cp+n) = 0; 642 first = cp += skip; 643 while(1) 644 { 645 while(!incmd) 646 { 647 if(cp>first) 648 { 649 count += (cp-first); 650 n = hist_ind(hp, ++hp->histind); 651#ifdef future 652 if(count==hp->histcmds[n]) 653 { 654 sfprintf(sfstderr,"count match n=%d\n",n); 655 if(histinit) 656 { 657 histinit = 0; 658 return; 659 } 660 } 661 else if(n>=histinit) 662#endif 663 hp->histcmds[n] = count; 664 first = cp; 665 } 666 switch(*((unsigned char*)(cp++))) 667 { 668 case HIST_CMDNO: 669 if(*cp==0) 670 { 671 hp->histmarker=count+2; 672 cp += (HIST_MARKSZ-1); 673 hp->histind--; 674 if(cp <= endbuff) 675 { 676 unsigned char *marker = (unsigned char*)(cp-4); 677 hp->histind = ((marker[0]<<16)|(marker[1]<<8)|marker[2]); 678 } 679 } 680 break; 681 case HIST_UNDO: 682 if(*cp==0) 683 { 684 cp+=1; 685 hp->histind-=2; 686 } 687 break; 688 default: 689 cp--; 690 incmd = 1; 691 } 692 if(cp > endbuff) 693 { 694 cp++; 695 goto refill; 696 } 697 } 698 first = cp; 699 while(*cp++); 700 if(cp > endbuff) 701 break; 702 incmd = 0; 703 while(*cp==0) 704 { 705 if(++cp > endbuff) 706 goto refill; 707 } 708 } 709 refill: 710 count += (--cp-first); 711 skip = (cp-endbuff); 712 if(!incmd && !skip) 713 hp->histcmds[hist_ind(hp,++hp->histind)] = count; 714 } 715 hp->histcnt = count; 716 if(incmd && last) 717 { 718 sfputc(hp->histfp,0); 719 hist_cancel(hp); 720 count = 2; 721 skip = 0; 722 oldind -= hp->histind; 723 hp->histind = hp->histind-hp->histsize + oldind +2; 724 if(hp->histind<0) 725 hp->histind = 1; 726 if(last<0) 727 { 728 char buff[HIST_MARKSZ]; 729 int fd = open(hp->histname,O_RDWR); 730 if(fd>=0) 731 { 732 hist_marker(buff,hp->histind); 733 write(fd,(char*)hist_stamp,2); 734 write(fd,buff,HIST_MARKSZ); 735 close(fd); 736 } 737 } 738 last = 0; 739 goto again; 740 } 741} 742 743/* 744 * This routine will cause the previous command to be cancelled 745 */ 746 747void hist_cancel(register History_t *hp) 748{ 749 register int c; 750 if(!hp) 751 return; 752 sfputc(hp->histfp,HIST_UNDO); 753 sfputc(hp->histfp,0); 754 sfsync(hp->histfp); 755 hp->histcnt += 2; 756 c = hist_ind(hp,--hp->histind); 757 hp->histcmds[c] = hp->histcnt; 758} 759 760/* 761 * flush the current history command 762 */ 763 764void hist_flush(register History_t *hp) 765{ 766 register char *buff; 767 if(hp) 768 { 769 if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR)) 770 { 771 hp->histflush = sfvalue(hp->histfp)+1; 772 sfwrite(hp->histfp,buff,0); 773 } 774 else 775 hp->histflush=0; 776 if(sfsync(hp->histfp)<0) 777 { 778 hist_close(hp); 779 if(!sh_histinit(hp->histshell)) 780 sh_offoption(SH_HISTORY); 781 } 782 hp->histflush = 0; 783 } 784} 785 786/* 787 * This is the write discipline for the history file 788 * When called from hist_flush(), trailing newlines are deleted and 789 * a zero byte. Line sequencing is added as required 790 */ 791 792#ifdef SF_BUFCONST 793static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle) 794#else 795static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle) 796#endif 797{ 798 register History_t *hp = (History_t*)handle; 799 register char *bufptr = ((char*)buff)+insize; 800 register int c,size = insize; 801 register off_t cur; 802 int saved=0; 803 char saveptr[HIST_MARKSZ]; 804 if(!hp->histflush) 805 return(write(sffileno(iop),(char*)buff,size)); 806 if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0) 807 { 808 errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno); 809 return(-1); 810 } 811 hp->histcnt = cur; 812 /* remove whitespace from end of commands */ 813 while(--bufptr >= (char*)buff) 814 { 815 c= *bufptr; 816 if(!isspace(c)) 817 { 818 if(c=='\\' && *(bufptr+1)!='\n') 819 bufptr++; 820 break; 821 } 822 } 823 /* don't count empty lines */ 824 if(++bufptr <= (char*)buff) 825 return(insize); 826 *bufptr++ = '\n'; 827 *bufptr++ = 0; 828 size = bufptr - (char*)buff; 829#if SHOPT_AUDIT 830 if(hp->auditfp) 831 { 832 time_t t=time((time_t*)0); 833 sfprintf(hp->auditfp,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED)?shgd->euserid:shgd->userid,t,hp->tty,size,buff,0); 834 sfsync(hp->auditfp); 835 } 836#endif /* SHOPT_AUDIT */ 837#if SHOPT_ACCTFILE 838 if(acctfd) 839 { 840 int timechars, offset; 841 offset = staktell(); 842 stakputs(buff); 843 stakseek(staktell() - 1); 844 timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *))); 845 lseek(acctfd, (off_t)0, SEEK_END); 846 write(acctfd, stakptr(offset), size - 2 + timechars); 847 stakseek(offset); 848 849 } 850#endif /* SHOPT_ACCTFILE */ 851 if(size&01) 852 { 853 size++; 854 *bufptr++ = 0; 855 } 856 hp->histcnt += size; 857 c = hist_ind(hp,++hp->histind); 858 hp->histcmds[c] = hp->histcnt; 859 if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2) 860 { 861 memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ); 862 saved=1; 863 hp->histcnt += HIST_MARKSZ; 864 hist_marker(bufptr,hp->histind); 865 hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt; 866 size += HIST_MARKSZ; 867 } 868 errno = 0; 869 size = write(sffileno(iop),(char*)buff,size); 870 if(saved) 871 memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ); 872 if(size>=0) 873 { 874 hp->histwfail = 0; 875 return(insize); 876 } 877 return(-1); 878} 879 880/* 881 * Put history sequence number <n> into buffer <buff> 882 * The buffer must be large enough to hold HIST_MARKSZ chars 883 */ 884 885static void hist_marker(register char *buff,register long cmdno) 886{ 887 *buff++ = HIST_CMDNO; 888 *buff++ = 0; 889 *buff++ = (cmdno>>16); 890 *buff++ = (cmdno>>8); 891 *buff++ = cmdno; 892 *buff++ = 0; 893} 894 895/* 896 * return byte offset in history file for command <n> 897 */ 898off_t hist_tell(register History_t *hp, int n) 899{ 900 return(hp->histcmds[hist_ind(hp,n)]); 901} 902 903/* 904 * seek to the position of command <n> 905 */ 906off_t hist_seek(register History_t *hp, int n) 907{ 908 return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET)); 909} 910 911/* 912 * write the command starting at offset <offset> onto file <outfile>. 913 * if character <last> appears before newline it is deleted 914 * each new-line character is replaced with string <nl>. 915 */ 916 917void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl) 918{ 919 register int oldc=0; 920 register int c; 921 if(offset<0 || !hp) 922 { 923 sfputr(outfile,sh_translate(e_unknown),'\n'); 924 return; 925 } 926 sfseek(hp->histfp,offset,SEEK_SET); 927 while((c = sfgetc(hp->histfp)) != EOF) 928 { 929 if(c && oldc=='\n') 930 sfputr(outfile,nl,-1); 931 else if(last && (c==0 || (c=='\n' && oldc==last))) 932 return; 933 else if(oldc) 934 sfputc(outfile,oldc); 935 oldc = c; 936 if(c==0) 937 return; 938 } 939 return; 940} 941 942/* 943 * find index for last line with given string 944 * If flag==0 then line must begin with string 945 * direction < 1 for backwards search 946*/ 947 948Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction) 949{ 950 register int index2; 951 off_t offset; 952 int *coffset=0; 953 Histloc_t location; 954 location.hist_command = -1; 955 location.hist_char = 0; 956 location.hist_line = 0; 957 if(!hp) 958 return(location); 959 /* leading ^ means beginning of line unless escaped */ 960 if(flag) 961 { 962 index2 = *string; 963 if(index2=='\\') 964 string++; 965 else if(index2=='^') 966 { 967 flag=0; 968 string++; 969 } 970 } 971 if(flag) 972 coffset = &location.hist_char; 973 index2 = (int)hp->histind; 974 if(direction<0) 975 { 976 index2 -= hp->histsize; 977 if(index2<1) 978 index2 = 1; 979 if(index1 <= index2) 980 return(location); 981 } 982 else if(index1 >= index2) 983 return(location); 984 while(index1!=index2) 985 { 986 direction>0?++index1:--index1; 987 offset = hist_tell(hp,index1); 988 if((location.hist_line=hist_match(hp,offset,string,coffset))>=0) 989 { 990 location.hist_command = index1; 991 return(location); 992 } 993#if KSHELL 994 /* allow a search to be aborted */ 995 if(((Shell_t*)hp->histshell)->trapnote&SH_SIGSET) 996 break; 997#endif /* KSHELL */ 998 } 999 return(location); 1000} 1001 1002/* 1003 * search for <string> in history file starting at location <offset> 1004 * If coffset==0 then line must begin with string 1005 * returns the line number of the match if successful, otherwise -1 1006 */ 1007 1008int hist_match(register History_t *hp,off_t offset,char *string,int *coffset) 1009{ 1010 register unsigned char *first, *cp; 1011 register int m,n,c=1,line=0; 1012#if SHOPT_MULTIBYTE 1013 mbinit(); 1014#endif /* SHOPT_MULTIBYTE */ 1015 sfseek(hp->histfp,offset,SEEK_SET); 1016 if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0))) 1017 return(-1); 1018 m = sfvalue(hp->histfp); 1019 n = strlen(string); 1020 while(m > n) 1021 { 1022 if(*cp==*string && memcmp(cp,string,n)==0) 1023 { 1024 if(coffset) 1025 *coffset = (cp-first); 1026 return(line); 1027 } 1028 if(!coffset) 1029 break; 1030 if(*cp=='\n') 1031 line++; 1032#if SHOPT_MULTIBYTE 1033 if((c=mbsize(cp)) < 0) 1034 c = 1; 1035#endif /* SHOPT_MULTIBYTE */ 1036 cp += c; 1037 m -= c; 1038 } 1039 return(-1); 1040} 1041 1042 1043#if SHOPT_ESH || SHOPT_VSH 1044/* 1045 * copy command <command> from history file to s1 1046 * at most <size> characters copied 1047 * if s1==0 the number of lines for the command is returned 1048 * line=linenumber for emacs copy and only this line of command will be copied 1049 * line < 0 for full command copy 1050 * -1 returned if there is no history file 1051 */ 1052 1053int hist_copy(char *s1,int size,int command,int line) 1054{ 1055 register int c; 1056 register History_t *hp = shgd->hist_ptr; 1057 register int count = 0; 1058 register char *s1max = s1+size; 1059 if(!hp) 1060 return(-1); 1061 hist_seek(hp,command); 1062 while ((c = sfgetc(hp->histfp)) && c!=EOF) 1063 { 1064 if(c=='\n') 1065 { 1066 if(count++ ==line) 1067 break; 1068 else if(line >= 0) 1069 continue; 1070 } 1071 if(s1 && (line<0 || line==count)) 1072 { 1073 if(s1 >= s1max) 1074 { 1075 *--s1 = 0; 1076 break; 1077 } 1078 *s1++ = c; 1079 } 1080 1081 } 1082 sfseek(hp->histfp,(off_t)0,SEEK_END); 1083 if(s1==0) 1084 return(count); 1085 if(count && (c= *(s1-1)) == '\n') 1086 s1--; 1087 *s1 = '\0'; 1088 return(count); 1089} 1090 1091/* 1092 * return word number <word> from command number <command> 1093 */ 1094 1095char *hist_word(char *string,int size,int word) 1096{ 1097 register int c; 1098 register char *s1 = string; 1099 register unsigned char *cp = (unsigned char*)s1; 1100 register int flag = 0; 1101 History_t *hp = hist_ptr; 1102 if(!hp) 1103 return(NIL(char*)); 1104 hist_copy(string,size,(int)hp->histind-1,-1); 1105 for(;c = *cp;cp++) 1106 { 1107 c = isspace(c); 1108 if(c && flag) 1109 { 1110 *cp = 0; 1111 if(--word==0) 1112 break; 1113 flag = 0; 1114 } 1115 else if(c==0 && flag==0) 1116 { 1117 s1 = (char*)cp; 1118 flag++; 1119 } 1120 } 1121 *cp = 0; 1122 if(s1 != string) 1123 strcpy(string,s1); 1124 return(string); 1125} 1126 1127#endif /* SHOPT_ESH */ 1128 1129#if SHOPT_ESH 1130/* 1131 * given the current command and line number, 1132 * and number of lines back or foward, 1133 * compute the new command and line number. 1134 */ 1135 1136Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines) 1137{ 1138 Histloc_t next; 1139 line += lines; 1140 if(!hp) 1141 { 1142 command = -1; 1143 goto done; 1144 } 1145 if(lines > 0) 1146 { 1147 register int count; 1148 while(command <= hp->histind) 1149 { 1150 count = hist_copy(NIL(char*),0, command,-1); 1151 if(count > line) 1152 goto done; 1153 line -= count; 1154 command++; 1155 } 1156 } 1157 else 1158 { 1159 register int least = (int)hp->histind-hp->histsize; 1160 while(1) 1161 { 1162 if(line >=0) 1163 goto done; 1164 if(--command < least) 1165 break; 1166 line += hist_copy(NIL(char*),0, command,-1); 1167 } 1168 command = -1; 1169 } 1170done: 1171 next.hist_line = line; 1172 next.hist_command = command; 1173 return(next); 1174} 1175#endif /* SHOPT_ESH */ 1176 1177 1178/* 1179 * Handle history file exceptions 1180 */ 1181#ifdef SF_BUFCONST 1182static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle) 1183#else 1184static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle) 1185#endif 1186{ 1187 register int newfd,oldfd; 1188 History_t *hp = (History_t*)handle; 1189 if(type==SF_WRITE) 1190 { 1191 if(errno==ENOSPC || hp->histwfail++ >= 10) 1192 return(0); 1193 /* write failure could be NFS problem, try to re-open */ 1194 close(oldfd=sffileno(fp)); 1195 if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0) 1196 { 1197 if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd) 1198 return(-1); 1199 fcntl(oldfd,F_SETFD,FD_CLOEXEC); 1200 close(newfd); 1201 if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt) 1202 { 1203 register int index = hp->histind; 1204 lseek(oldfd,(off_t)2,SEEK_SET); 1205 hp->histcnt = 2; 1206 hp->histind = 1; 1207 hp->histcmds[1] = 2; 1208 hist_eof(hp); 1209 hp->histmarker = hp->histcnt; 1210 hp->histind = index; 1211 } 1212 return(1); 1213 } 1214 errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname); 1215 return(-1); 1216 } 1217 return(0); 1218} 1219