14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*8462SApril.Chin@Sun.COM * Copyright (c) 1982-2008 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 7*8462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * David Korn <dgk@research.att.com> * 184887Schin * * 194887Schin ***********************************************************************/ 204887Schin #pragma prototyped 214887Schin /* 224887Schin * History file manipulation routines 234887Schin * 244887Schin * David Korn 254887Schin * AT&T Labs 264887Schin * 274887Schin */ 284887Schin 294887Schin /* 304887Schin * Each command in the history file starts on an even byte is null terminated. 314887Schin * The first byte must contain the special character HIST_UNDO and the second 324887Schin * byte is the version number. The sequence HIST_UNDO 0, following a command, 334887Schin * nullifies the previous command. A six byte sequence starting with 344887Schin * HIST_CMDNO is used to store the command number so that it is not necessary 354887Schin * to read the file from beginning to end to get to the last block of 364887Schin * commands. This format of this sequence is different in version 1 374887Schin * then in version 0. Version 1 allows commands to use the full 8 bit 384887Schin * character set. It can understand version 0 format files. 394887Schin */ 404887Schin 414887Schin 424887Schin #define HIST_MAX (sizeof(int)*HIST_BSIZE) 434887Schin #define HIST_BIG (0100000-1024) /* 1K less than maximum short */ 444887Schin #define HIST_LINE 32 /* typical length for history line */ 454887Schin #define HIST_MARKSZ 6 464887Schin #define HIST_RECENT 600 474887Schin #define HIST_UNDO 0201 /* invalidate previous command */ 484887Schin #define HIST_CMDNO 0202 /* next 3 bytes give command number */ 494887Schin #define HIST_BSIZE 4096 /* size of history file buffer */ 504887Schin #define HIST_DFLT 512 /* default size of history list */ 514887Schin 52*8462SApril.Chin@Sun.COM #if SHOPT_AUDIT 53*8462SApril.Chin@Sun.COM # define _HIST_AUDIT Sfio_t *auditfp; \ 54*8462SApril.Chin@Sun.COM char *tty; \ 55*8462SApril.Chin@Sun.COM int auditmask; 56*8462SApril.Chin@Sun.COM #else 57*8462SApril.Chin@Sun.COM # define _HIST_AUDIT 58*8462SApril.Chin@Sun.COM #endif 59*8462SApril.Chin@Sun.COM 604887Schin #define _HIST_PRIVATE \ 61*8462SApril.Chin@Sun.COM void *histshell; \ 624887Schin off_t histcnt; /* offset into history file */\ 634887Schin off_t histmarker; /* offset of last command marker */ \ 644887Schin int histflush; /* set if flushed outside of hflush() */\ 654887Schin int histmask; /* power of two mask for histcnt */ \ 664887Schin char histbuff[HIST_BSIZE+1]; /* history file buffer */ \ 674887Schin int histwfail; \ 68*8462SApril.Chin@Sun.COM _HIST_AUDIT \ 694887Schin off_t histcmds[2]; /* offset for recent commands, must be last */ 704887Schin 714887Schin #define hist_ind(hp,c) ((int)((c)&(hp)->histmask)) 724887Schin 734887Schin #include <ast.h> 744887Schin #include <sfio.h> 754887Schin #include "FEATURE/time" 764887Schin #include <error.h> 774887Schin #include <ctype.h> 784887Schin #include <ls.h> 794887Schin #if KSHELL 804887Schin # include "defs.h" 814887Schin # include "variables.h" 824887Schin # include "path.h" 834887Schin # include "builtins.h" 844887Schin # include "io.h" 854887Schin #endif /* KSHELL */ 864887Schin #include "history.h" 874887Schin 884887Schin #if !KSHELL 894887Schin # define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x))) 904887Schin # define NIL(type) ((type)0) 914887Schin # define path_relative(x) (x) 924887Schin # ifdef __STDC__ 934887Schin # define nv_getval(s) getenv(#s) 944887Schin # else 954887Schin # define nv_getval(s) getenv("s") 964887Schin # endif /* __STDC__ */ 974887Schin # define e_unknown "unknown" 984887Schin # define sh_translate(x) (x) 994887Schin char login_sh = 0; 1004887Schin char hist_fname[] = "/.history"; 1014887Schin #endif /* KSHELL */ 1024887Schin 1034887Schin #ifndef O_BINARY 1044887Schin # define O_BINARY 0 1054887Schin #endif /* O_BINARY */ 1064887Schin 1074887Schin int _Hist = 0; 1084887Schin static void hist_marker(char*,long); 109*8462SApril.Chin@Sun.COM static History_t* hist_trim(History_t*, int); 1104887Schin static int hist_nearend(History_t*,Sfio_t*, off_t); 1114887Schin static int hist_check(int); 1124887Schin static int hist_clean(int); 1134887Schin #ifdef SF_BUFCONST 1144887Schin static ssize_t hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*); 1154887Schin static int hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*); 1164887Schin #else 1174887Schin static int hist_write(Sfio_t*, const void*, int, Sfdisc_t*); 1184887Schin static int hist_exceptf(Sfio_t*, int, Sfdisc_t*); 1194887Schin #endif 1204887Schin 1214887Schin 1224887Schin static int histinit; 1234887Schin static mode_t histmode; 1244887Schin static History_t *wasopen; 1254887Schin static History_t *hist_ptr; 1264887Schin 1274887Schin #if SHOPT_ACCTFILE 1284887Schin static int acctfd; 1294887Schin static char *logname; 1304887Schin # include <pwd.h> 1314887Schin 132*8462SApril.Chin@Sun.COM static int acctinit(History_t *hp) 1334887Schin { 1344887Schin register char *cp, *acctfile; 135*8462SApril.Chin@Sun.COM Namval_t *np = nv_search("ACCTFILE",((Shell_t*)hp->histshell)->var_tree,0); 1364887Schin 1374887Schin if(!np || !(acctfile=nv_getval(np))) 1384887Schin return(0); 1394887Schin if(!(cp = getlogin())) 1404887Schin { 1414887Schin struct passwd *userinfo = getpwuid(getuid()); 1424887Schin if(userinfo) 1434887Schin cp = userinfo->pw_name; 1444887Schin else 1454887Schin cp = "unknown"; 1464887Schin } 1474887Schin logname = strdup(cp); 1484887Schin if((acctfd=sh_open(acctfile, 1494887Schin O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && 1504887Schin (unsigned)acctfd < 10) 1514887Schin { 1524887Schin int n; 1534887Schin if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0) 1544887Schin { 1554887Schin close(acctfd); 1564887Schin acctfd = n; 1574887Schin } 1584887Schin } 1594887Schin if(acctfd < 0) 1604887Schin { 1614887Schin acctfd = 0; 1624887Schin return(0); 1634887Schin } 1644887Schin if(strmatch(acctfile,e_devfdNN)) 1654887Schin { 1664887Schin char newfile[16]; 1674887Schin sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd); 1684887Schin nv_putval(np,newfile,NV_RDONLY); 1694887Schin } 1704887Schin else 1714887Schin fcntl(acctfd,F_SETFD,FD_CLOEXEC); 1724887Schin return(1); 1734887Schin } 1744887Schin #endif /* SHOPT_ACCTFILE */ 1754887Schin 176*8462SApril.Chin@Sun.COM #if SHOPT_AUDIT 177*8462SApril.Chin@Sun.COM static int sh_checkaudit(History_t *hp, const char *name, char *logbuf, size_t len) 178*8462SApril.Chin@Sun.COM { 179*8462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)hp->histshell; 180*8462SApril.Chin@Sun.COM char *buff, *cp, *last; 181*8462SApril.Chin@Sun.COM int id1, id2, r=0, n, fd; 182*8462SApril.Chin@Sun.COM if((fd=open(name, O_RDONLY)) < 0) 183*8462SApril.Chin@Sun.COM return(0); 184*8462SApril.Chin@Sun.COM if((n = read(fd, logbuf,len-1)) < 0) 185*8462SApril.Chin@Sun.COM goto done; 186*8462SApril.Chin@Sun.COM while(logbuf[n-1]=='\n') 187*8462SApril.Chin@Sun.COM n--; 188*8462SApril.Chin@Sun.COM logbuf[n] = 0; 189*8462SApril.Chin@Sun.COM if(!(cp=strchr(logbuf,';')) && !(cp=strchr(logbuf,' '))) 190*8462SApril.Chin@Sun.COM goto done; 191*8462SApril.Chin@Sun.COM *cp = 0; 192*8462SApril.Chin@Sun.COM do 193*8462SApril.Chin@Sun.COM { 194*8462SApril.Chin@Sun.COM cp++; 195*8462SApril.Chin@Sun.COM id1 = id2 = strtol(cp,&last,10); 196*8462SApril.Chin@Sun.COM if(*last=='-') 197*8462SApril.Chin@Sun.COM id1 = strtol(last+1,&last,10); 198*8462SApril.Chin@Sun.COM if(shp->euserid >=id1 && shp->euserid <= id2) 199*8462SApril.Chin@Sun.COM r |= 1; 200*8462SApril.Chin@Sun.COM if(shp->userid >=id1 && shp->userid <= id2) 201*8462SApril.Chin@Sun.COM r |= 2; 202*8462SApril.Chin@Sun.COM cp = last; 203*8462SApril.Chin@Sun.COM } 204*8462SApril.Chin@Sun.COM while(*cp==';' || *cp==' '); 205*8462SApril.Chin@Sun.COM done: 206*8462SApril.Chin@Sun.COM close(fd); 207*8462SApril.Chin@Sun.COM return(r); 208*8462SApril.Chin@Sun.COM 209*8462SApril.Chin@Sun.COM } 210*8462SApril.Chin@Sun.COM #endif /*SHOPT_AUDIT*/ 211*8462SApril.Chin@Sun.COM 2124887Schin static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; 2134887Schin static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; 2144887Schin 2154887Schin static void hist_touch(void *handle) 2164887Schin { 2174887Schin touch((char*)handle, (time_t)0, (time_t)0, 0); 2184887Schin } 2194887Schin 2204887Schin /* 2214887Schin * open the history file 2224887Schin * if HISTNAME is not given and userid==0 then no history file. 2234887Schin * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is 2244887Schin * cleaned up. 2254887Schin * hist_open() returns 1, if history file is open 2264887Schin */ 227*8462SApril.Chin@Sun.COM int sh_histinit(void *sh_context) 2284887Schin { 229*8462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)sh_context; 2304887Schin register int fd; 2314887Schin register History_t *hp; 2324887Schin register char *histname; 2334887Schin char *fname=0; 2344887Schin int histmask, maxlines, hist_start=0; 2354887Schin register char *cp; 2364887Schin register off_t hsize = 0; 2374887Schin 238*8462SApril.Chin@Sun.COM if(shp->hist_ptr=hist_ptr) 2394887Schin return(1); 2404887Schin if(!(histname = nv_getval(HISTFILE))) 2414887Schin { 2424887Schin int offset = staktell(); 2434887Schin if(cp=nv_getval(HOME)) 2444887Schin stakputs(cp); 2454887Schin stakputs(hist_fname); 2464887Schin stakputc(0); 2474887Schin stakseek(offset); 2484887Schin histname = stakptr(offset); 2494887Schin } 2504887Schin #ifdef future 2514887Schin if(hp=wasopen) 2524887Schin { 2534887Schin /* reuse history file if same name */ 2544887Schin wasopen = 0; 255*8462SApril.Chin@Sun.COM shp->hist_ptr = hist_ptr = hp; 2564887Schin if(strcmp(histname,hp->histname)==0) 2574887Schin return(1); 2584887Schin else 2594887Schin hist_free(); 2604887Schin } 2614887Schin #endif 2624887Schin retry: 2634887Schin cp = path_relative(histname); 2644887Schin if(!histinit) 2654887Schin histmode = S_IRUSR|S_IWUSR; 2664887Schin if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) 2674887Schin { 2684887Schin hsize=lseek(fd,(off_t)0,SEEK_END); 2694887Schin } 2704887Schin if((unsigned)fd <=2) 2714887Schin { 2724887Schin int n; 2734887Schin if((n=fcntl(fd,F_DUPFD,10))>=0) 2744887Schin { 2754887Schin close(fd); 2764887Schin fd=n; 2774887Schin } 2784887Schin } 2794887Schin /* make sure that file has history file format */ 2804887Schin if(hsize && hist_check(fd)) 2814887Schin { 2824887Schin close(fd); 2834887Schin hsize = 0; 2844887Schin if(unlink(cp)>=0) 2854887Schin goto retry; 2864887Schin fd = -1; 2874887Schin } 2884887Schin if(fd < 0) 2894887Schin { 2904887Schin #if KSHELL 2914887Schin /* don't allow root a history_file in /tmp */ 292*8462SApril.Chin@Sun.COM if(shp->userid) 2934887Schin #endif /* KSHELL */ 2944887Schin { 2954887Schin if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) 2964887Schin return(0); 2974887Schin fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); 2984887Schin } 2994887Schin } 3004887Schin if(fd<0) 3014887Schin return(0); 3024887Schin /* set the file to close-on-exec */ 3034887Schin fcntl(fd,F_SETFD,FD_CLOEXEC); 3044887Schin if(cp=nv_getval(HISTSIZE)) 3054887Schin maxlines = (unsigned)strtol(cp, (char**)0, 10); 3064887Schin else 3074887Schin maxlines = HIST_DFLT; 3084887Schin for(histmask=16;histmask <= maxlines; histmask <<=1 ); 3094887Schin if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) 3104887Schin { 3114887Schin close(fd); 3124887Schin return(0); 3134887Schin } 314*8462SApril.Chin@Sun.COM shp->hist_ptr = hist_ptr = hp; 315*8462SApril.Chin@Sun.COM hp->histshell = (void*)shp; 3164887Schin hp->histsize = maxlines; 3174887Schin hp->histmask = histmask; 3184887Schin hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); 3194887Schin memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); 3204887Schin hp->histind = 1; 3214887Schin hp->histcmds[1] = 2; 3224887Schin hp->histcnt = 2; 3234887Schin hp->histname = strdup(histname); 3244887Schin hp->histdisc = hist_disc; 3254887Schin if(hsize==0) 3264887Schin { 3274887Schin /* put special characters at front of file */ 3284887Schin sfwrite(hp->histfp,(char*)hist_stamp,2); 3294887Schin sfsync(hp->histfp); 3304887Schin } 3314887Schin /* initialize history list */ 3324887Schin else 3334887Schin { 3344887Schin int first,last; 3354887Schin off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; 3364887Schin hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); 3374887Schin hist_eof(hp); /* this sets histind to last command */ 3384887Schin if((hist_start = (last=(int)hp->histind)-maxlines) <=0) 3394887Schin hist_start = 1; 3404887Schin mark = hp->histmarker; 3414887Schin while(first > hist_start) 3424887Schin { 3434887Schin size += size; 3444887Schin first = hist_nearend(hp,hp->histfp,hsize-size); 3454887Schin hp->histind = first; 3464887Schin } 3474887Schin histinit = hist_start; 3484887Schin hist_eof(hp); 3494887Schin if(!histinit) 3504887Schin { 3514887Schin sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); 3524887Schin hp->histind = last; 3534887Schin hp->histmarker = mark; 3544887Schin } 3554887Schin histinit = 0; 3564887Schin } 3574887Schin if(fname) 3584887Schin { 3594887Schin unlink(fname); 3604887Schin free((void*)fname); 3614887Schin } 3624887Schin if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) 3634887Schin { 3644887Schin #ifdef DEBUG 3654887Schin sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); 3664887Schin sfsync(sfstderr); 3674887Schin #endif /* DEBUG */ 368*8462SApril.Chin@Sun.COM hp = hist_trim(hp,(int)hp->histind-maxlines); 3694887Schin } 3704887Schin sfdisc(hp->histfp,&hp->histdisc); 3714887Schin #if KSHELL 3724887Schin (HISTCUR)->nvalue.lp = (&hp->histind); 3734887Schin #endif /* KSHELL */ 3744887Schin sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); 3754887Schin #if SHOPT_ACCTFILE 3764887Schin if(sh_isstate(SH_INTERACTIVE)) 377*8462SApril.Chin@Sun.COM acctinit(hp); 3784887Schin #endif /* SHOPT_ACCTFILE */ 379*8462SApril.Chin@Sun.COM #if SHOPT_AUDIT 380*8462SApril.Chin@Sun.COM { 381*8462SApril.Chin@Sun.COM char buff[SF_BUFSIZE]; 382*8462SApril.Chin@Sun.COM hp->auditfp = 0; 383*8462SApril.Chin@Sun.COM if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) 384*8462SApril.Chin@Sun.COM { 385*8462SApril.Chin@Sun.COM if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) 386*8462SApril.Chin@Sun.COM { 387*8462SApril.Chin@Sun.COM int n; 388*8462SApril.Chin@Sun.COM if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) 389*8462SApril.Chin@Sun.COM { 390*8462SApril.Chin@Sun.COM sh_close(fd); 391*8462SApril.Chin@Sun.COM fd = n; 392*8462SApril.Chin@Sun.COM } 393*8462SApril.Chin@Sun.COM } 394*8462SApril.Chin@Sun.COM if(fd>=0) 395*8462SApril.Chin@Sun.COM { 396*8462SApril.Chin@Sun.COM hp->tty = strdup(ttyname(2)); 397*8462SApril.Chin@Sun.COM hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); 398*8462SApril.Chin@Sun.COM } 399*8462SApril.Chin@Sun.COM } 400*8462SApril.Chin@Sun.COM } 401*8462SApril.Chin@Sun.COM #endif 4024887Schin return(1); 4034887Schin } 4044887Schin 4054887Schin /* 4064887Schin * close the history file and free the space 4074887Schin */ 4084887Schin 4094887Schin void hist_close(register History_t *hp) 4104887Schin { 411*8462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)hp->histshell; 4124887Schin sfclose(hp->histfp); 413*8462SApril.Chin@Sun.COM #if SHOPT_AUDIT 414*8462SApril.Chin@Sun.COM if(hp->auditfp) 415*8462SApril.Chin@Sun.COM { 416*8462SApril.Chin@Sun.COM if(hp->tty) 417*8462SApril.Chin@Sun.COM free((void*)hp->tty); 418*8462SApril.Chin@Sun.COM sfclose(hp->auditfp); 419*8462SApril.Chin@Sun.COM } 420*8462SApril.Chin@Sun.COM #endif /* SHOPT_AUDIT */ 4214887Schin free((char*)hp); 4224887Schin hist_ptr = 0; 423*8462SApril.Chin@Sun.COM shp->hist_ptr = 0; 4244887Schin #if SHOPT_ACCTFILE 4254887Schin if(acctfd) 4264887Schin { 4274887Schin close(acctfd); 4284887Schin acctfd = 0; 4294887Schin } 4304887Schin #endif /* SHOPT_ACCTFILE */ 4314887Schin } 4324887Schin 4334887Schin /* 4344887Schin * check history file format to see if it begins with special byte 4354887Schin */ 4364887Schin static int hist_check(register int fd) 4374887Schin { 4384887Schin unsigned char magic[2]; 4394887Schin lseek(fd,(off_t)0,SEEK_SET); 4404887Schin if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO)) 4414887Schin return(1); 4424887Schin return(0); 4434887Schin } 4444887Schin 4454887Schin /* 4464887Schin * clean out history file OK if not modified in HIST_RECENT seconds 4474887Schin */ 4484887Schin static int hist_clean(int fd) 4494887Schin { 4504887Schin struct stat statb; 4514887Schin return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT); 4524887Schin } 4534887Schin 4544887Schin /* 4554887Schin * Copy the last <n> commands to a new file and make this the history file 4564887Schin */ 4574887Schin 458*8462SApril.Chin@Sun.COM static History_t* hist_trim(History_t *hp, int n) 4594887Schin { 4604887Schin register char *cp; 4614887Schin register int incmd=1, c=0; 4624887Schin register History_t *hist_new, *hist_old = hp; 4634887Schin char *buff, *endbuff, *tmpname=0; 4644887Schin off_t oldp,newp; 4654887Schin struct stat statb; 4664887Schin unlink(hist_old->histname); 4674887Schin if(access(hist_old->histname,F_OK) >= 0) 4684887Schin { 4694887Schin /* The unlink can fail on windows 95 */ 4704887Schin int fd; 4714887Schin char *last, *name=hist_old->histname; 4724887Schin close(sffileno(hist_old->histfp)); 4734887Schin tmpname = (char*)malloc(strlen(name)+14); 4744887Schin if(last = strrchr(name,'/')) 4754887Schin { 4764887Schin *last = 0; 4774887Schin pathtmp(tmpname,name,"hist",NIL(int*)); 4784887Schin *last = '/'; 4794887Schin } 4804887Schin else 4814887Schin pathtmp(tmpname,".","hist",NIL(int*)); 4824887Schin if(rename(name,tmpname) < 0) 4834887Schin tmpname = name; 4844887Schin fd = open(tmpname,O_RDONLY); 4854887Schin sfsetfd(hist_old->histfp,fd); 4864887Schin if(tmpname==name) 4874887Schin tmpname = 0; 4884887Schin } 489*8462SApril.Chin@Sun.COM hist_ptr = 0; 4904887Schin if(fstat(sffileno(hist_old->histfp),&statb)>=0) 4914887Schin { 4924887Schin histinit = 1; 4934887Schin histmode = statb.st_mode; 4944887Schin } 495*8462SApril.Chin@Sun.COM if(!sh_histinit(hp->histshell)) 4964887Schin { 4974887Schin /* use the old history file */ 498*8462SApril.Chin@Sun.COM return hist_ptr = hist_old; 4994887Schin } 5004887Schin hist_new = hist_ptr; 5014887Schin hist_ptr = hist_old; 5024887Schin if(--n < 0) 5034887Schin n = 0; 5044887Schin newp = hist_seek(hist_old,++n); 5054887Schin while(1) 5064887Schin { 5074887Schin if(!incmd) 5084887Schin { 5094887Schin c = hist_ind(hist_new,++hist_new->histind); 5104887Schin hist_new->histcmds[c] = hist_new->histcnt; 5114887Schin if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2) 5124887Schin { 5134887Schin char locbuff[HIST_MARKSZ]; 5144887Schin hist_marker(locbuff,hist_new->histind); 5154887Schin sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ); 5164887Schin hist_new->histcnt += HIST_MARKSZ; 5174887Schin hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt; 5184887Schin } 5194887Schin oldp = newp; 5204887Schin newp = hist_seek(hist_old,++n); 5214887Schin if(newp <=oldp) 5224887Schin break; 5234887Schin } 5244887Schin if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0))) 5254887Schin break; 5264887Schin *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0; 5274887Schin /* copy to null byte */ 5284887Schin incmd = 0; 5294887Schin while(*cp++); 5304887Schin if(cp > endbuff) 5314887Schin incmd = 1; 5324887Schin else if(*cp==0) 5334887Schin cp++; 5344887Schin if(cp > endbuff) 5354887Schin cp = endbuff; 5364887Schin c = cp-buff; 5374887Schin hist_new->histcnt += c; 5384887Schin sfwrite(hist_new->histfp,buff,c); 5394887Schin } 540*8462SApril.Chin@Sun.COM hist_cancel(hist_new); 5414887Schin sfclose(hist_old->histfp); 5424887Schin if(tmpname) 5434887Schin { 5444887Schin unlink(tmpname); 5454887Schin free(tmpname); 5464887Schin } 5474887Schin free((char*)hist_old); 548*8462SApril.Chin@Sun.COM return hist_ptr = hist_new; 5494887Schin } 5504887Schin 5514887Schin /* 5524887Schin * position history file at size and find next command number 5534887Schin */ 5544887Schin static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size) 5554887Schin { 5564887Schin register unsigned char *cp, *endbuff; 5574887Schin register int n, incmd=1; 5584887Schin unsigned char *buff, marker[4]; 5594887Schin if(size <= 2L || sfseek(iop,size,SEEK_SET)<0) 5604887Schin goto begin; 5614887Schin /* skip to marker command and return the number */ 5624887Schin /* numbering commands occur after a null and begin with HIST_CMDNO */ 5634887Schin while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR)) 5644887Schin { 5654887Schin n = sfvalue(iop); 5664887Schin *(endbuff=cp+n) = 0; 5674887Schin while(1) 5684887Schin { 5694887Schin /* check for marker */ 5704887Schin if(!incmd && *cp++==HIST_CMDNO && *cp==0) 5714887Schin { 5724887Schin n = cp+1 - buff; 5734887Schin incmd = -1; 5744887Schin break; 5754887Schin } 5764887Schin incmd = 0; 5774887Schin while(*cp++); 5784887Schin if(cp>endbuff) 5794887Schin { 5804887Schin incmd = 1; 5814887Schin break; 5824887Schin } 5834887Schin if(*cp==0 && ++cp>endbuff) 5844887Schin break; 5854887Schin } 5864887Schin size += n; 5874887Schin sfread(iop,(char*)buff,n); 5884887Schin if(incmd < 0) 5894887Schin { 5904887Schin if((n=sfread(iop,(char*)marker,4))==4) 5914887Schin { 5924887Schin n = (marker[0]<<16)|(marker[1]<<8)|marker[2]; 5934887Schin if(n < size/2) 5944887Schin { 5954887Schin hp->histmarker = hp->histcnt = size+4; 5964887Schin return(n); 5974887Schin } 5984887Schin n=4; 5994887Schin } 6004887Schin if(n >0) 6014887Schin size += n; 6024887Schin incmd = 0; 6034887Schin } 6044887Schin } 6054887Schin begin: 6064887Schin sfseek(iop,(off_t)2,SEEK_SET); 6074887Schin hp->histmarker = hp->histcnt = 2L; 6084887Schin return(1); 6094887Schin } 6104887Schin 6114887Schin /* 6124887Schin * This routine reads the history file from the present position 6134887Schin * to the end-of-file and puts the information in the in-core 6144887Schin * history table 6154887Schin * Note that HIST_CMDNO is only recognized at the beginning of a command 6164887Schin * and that HIST_UNDO as the first character of a command is skipped 6174887Schin * unless it is followed by 0. If followed by 0 then it cancels 6184887Schin * the previous command. 6194887Schin */ 6204887Schin 6214887Schin void hist_eof(register History_t *hp) 6224887Schin { 6234887Schin register char *cp,*first,*endbuff; 6244887Schin register int incmd = 0; 6254887Schin register off_t count = hp->histcnt; 6264887Schin int n,skip=0; 6274887Schin sfseek(hp->histfp,count,SEEK_SET); 6284887Schin while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0)) 6294887Schin { 6304887Schin n = sfvalue(hp->histfp); 6314887Schin *(endbuff = cp+n) = 0; 6324887Schin first = cp += skip; 6334887Schin while(1) 6344887Schin { 6354887Schin while(!incmd) 6364887Schin { 6374887Schin if(cp>first) 6384887Schin { 6394887Schin count += (cp-first); 6404887Schin n = hist_ind(hp, ++hp->histind); 6414887Schin #ifdef future 6424887Schin if(count==hp->histcmds[n]) 6434887Schin { 6444887Schin sfprintf(sfstderr,"count match n=%d\n",n); 6454887Schin if(histinit) 6464887Schin { 6474887Schin histinit = 0; 6484887Schin return; 6494887Schin } 6504887Schin } 6514887Schin else if(n>=histinit) 6524887Schin #endif 6534887Schin hp->histcmds[n] = count; 6544887Schin first = cp; 6554887Schin } 6564887Schin switch(*((unsigned char*)(cp++))) 6574887Schin { 6584887Schin case HIST_CMDNO: 6594887Schin if(*cp==0) 6604887Schin { 6614887Schin hp->histmarker=count+2; 6624887Schin cp += (HIST_MARKSZ-1); 6634887Schin hp->histind--; 6644887Schin #ifdef future 6654887Schin if(cp <= endbuff) 6664887Schin { 6674887Schin unsigned char *marker = (unsigned char*)(cp-4); 6684887Schin int n = ((marker[0]<<16) 6694887Schin |(marker[1]<<8)|marker[2]); 6704887Schin if((n<count/2) && n != (hp->histind+1)) 6714887Schin errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n); 6724887Schin } 6734887Schin #endif 6744887Schin } 6754887Schin break; 6764887Schin case HIST_UNDO: 6774887Schin if(*cp==0) 6784887Schin { 6794887Schin cp+=1; 6804887Schin hp->histind-=2; 6814887Schin } 6824887Schin break; 6834887Schin default: 6844887Schin cp--; 6854887Schin incmd = 1; 6864887Schin } 6874887Schin if(cp > endbuff) 6884887Schin { 6894887Schin cp++; 6904887Schin goto refill; 6914887Schin } 6924887Schin } 6934887Schin first = cp; 6944887Schin while(*cp++); 6954887Schin if(cp > endbuff) 6964887Schin break; 6974887Schin incmd = 0; 6984887Schin while(*cp==0) 6994887Schin { 7004887Schin if(++cp > endbuff) 7014887Schin goto refill; 7024887Schin } 7034887Schin } 7044887Schin refill: 7054887Schin count += (--cp-first); 7064887Schin skip = (cp-endbuff); 7074887Schin if(!incmd && !skip) 7084887Schin hp->histcmds[hist_ind(hp,++hp->histind)] = count; 7094887Schin } 7104887Schin hp->histcnt = count; 7114887Schin } 7124887Schin 7134887Schin /* 7144887Schin * This routine will cause the previous command to be cancelled 7154887Schin */ 7164887Schin 7174887Schin void hist_cancel(register History_t *hp) 7184887Schin { 7194887Schin register int c; 7204887Schin if(!hp) 7214887Schin return; 7224887Schin sfputc(hp->histfp,HIST_UNDO); 7234887Schin sfputc(hp->histfp,0); 7244887Schin sfsync(hp->histfp); 7254887Schin hp->histcnt += 2; 7264887Schin c = hist_ind(hp,--hp->histind); 7274887Schin hp->histcmds[c] = hp->histcnt; 7284887Schin } 7294887Schin 7304887Schin /* 7314887Schin * flush the current history command 7324887Schin */ 7334887Schin 7344887Schin void hist_flush(register History_t *hp) 7354887Schin { 7364887Schin register char *buff; 7374887Schin if(hp) 7384887Schin { 7394887Schin if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR)) 7404887Schin { 7414887Schin hp->histflush = sfvalue(hp->histfp)+1; 7424887Schin sfwrite(hp->histfp,buff,0); 7434887Schin } 7444887Schin else 7454887Schin hp->histflush=0; 7464887Schin if(sfsync(hp->histfp)<0) 7474887Schin { 7484887Schin hist_close(hp); 749*8462SApril.Chin@Sun.COM if(!sh_histinit(hp->histshell)) 7504887Schin sh_offoption(SH_HISTORY); 7514887Schin } 7524887Schin hp->histflush = 0; 7534887Schin } 7544887Schin } 7554887Schin 7564887Schin /* 7574887Schin * This is the write discipline for the history file 7584887Schin * When called from hist_flush(), trailing newlines are deleted and 7594887Schin * a zero byte. Line sequencing is added as required 7604887Schin */ 7614887Schin 7624887Schin #ifdef SF_BUFCONST 7634887Schin static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle) 7644887Schin #else 7654887Schin static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle) 7664887Schin #endif 7674887Schin { 7684887Schin register History_t *hp = (History_t*)handle; 7694887Schin register char *bufptr = ((char*)buff)+insize; 7704887Schin register int c,size = insize; 7714887Schin register off_t cur; 7724887Schin int saved=0; 7734887Schin char saveptr[HIST_MARKSZ]; 7744887Schin if(!hp->histflush) 7754887Schin return(write(sffileno(iop),(char*)buff,size)); 7764887Schin if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0) 7774887Schin { 7784887Schin errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno); 7794887Schin return(-1); 7804887Schin } 7814887Schin hp->histcnt = cur; 7824887Schin /* remove whitespace from end of commands */ 7834887Schin while(--bufptr >= (char*)buff) 7844887Schin { 7854887Schin c= *bufptr; 7864887Schin if(!isspace(c)) 7874887Schin { 7884887Schin if(c=='\\' && *(bufptr+1)!='\n') 7894887Schin bufptr++; 7904887Schin break; 7914887Schin } 7924887Schin } 7934887Schin /* don't count empty lines */ 7944887Schin if(++bufptr <= (char*)buff) 7954887Schin return(insize); 7964887Schin *bufptr++ = '\n'; 7974887Schin *bufptr++ = 0; 7984887Schin size = bufptr - (char*)buff; 799*8462SApril.Chin@Sun.COM #if SHOPT_AUDIT 800*8462SApril.Chin@Sun.COM if(hp->auditfp) 801*8462SApril.Chin@Sun.COM { 802*8462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)hp->histshell; 803*8462SApril.Chin@Sun.COM time_t t=time((time_t*)0); 804*8462SApril.Chin@Sun.COM sfprintf(hp->auditfp,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED)?shp->euserid:shp->userid,t,hp->tty,size,buff,0); 805*8462SApril.Chin@Sun.COM sfsync(hp->auditfp); 806*8462SApril.Chin@Sun.COM } 807*8462SApril.Chin@Sun.COM #endif /* SHOPT_AUDIT */ 8084887Schin #if SHOPT_ACCTFILE 8094887Schin if(acctfd) 8104887Schin { 8114887Schin int timechars, offset; 8124887Schin offset = staktell(); 8134887Schin stakputs(buff); 8144887Schin stakseek(staktell() - 1); 8154887Schin timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *))); 8164887Schin lseek(acctfd, (off_t)0, SEEK_END); 8174887Schin write(acctfd, stakptr(offset), size - 2 + timechars); 8184887Schin stakseek(offset); 8194887Schin 8204887Schin } 8214887Schin #endif /* SHOPT_ACCTFILE */ 8224887Schin if(size&01) 8234887Schin { 8244887Schin size++; 8254887Schin *bufptr++ = 0; 8264887Schin } 8274887Schin hp->histcnt += size; 8284887Schin c = hist_ind(hp,++hp->histind); 8294887Schin hp->histcmds[c] = hp->histcnt; 8304887Schin if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2) 8314887Schin { 8324887Schin memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ); 8334887Schin saved=1; 8344887Schin hp->histcnt += HIST_MARKSZ; 8354887Schin hist_marker(bufptr,hp->histind); 8364887Schin hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt; 8374887Schin size += HIST_MARKSZ; 8384887Schin } 8394887Schin errno = 0; 8404887Schin size = write(sffileno(iop),(char*)buff,size); 8414887Schin if(saved) 8424887Schin memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ); 8434887Schin if(size>=0) 8444887Schin { 8454887Schin hp->histwfail = 0; 8464887Schin return(insize); 8474887Schin } 8484887Schin return(-1); 8494887Schin } 8504887Schin 8514887Schin /* 8524887Schin * Put history sequence number <n> into buffer <buff> 8534887Schin * The buffer must be large enough to hold HIST_MARKSZ chars 8544887Schin */ 8554887Schin 8564887Schin static void hist_marker(register char *buff,register long cmdno) 8574887Schin { 8584887Schin *buff++ = HIST_CMDNO; 8594887Schin *buff++ = 0; 8604887Schin *buff++ = (cmdno>>16); 8614887Schin *buff++ = (cmdno>>8); 8624887Schin *buff++ = cmdno; 8634887Schin *buff++ = 0; 8644887Schin } 8654887Schin 8664887Schin /* 8674887Schin * return byte offset in history file for command <n> 8684887Schin */ 8694887Schin off_t hist_tell(register History_t *hp, int n) 8704887Schin { 8714887Schin return(hp->histcmds[hist_ind(hp,n)]); 8724887Schin } 8734887Schin 8744887Schin /* 8754887Schin * seek to the position of command <n> 8764887Schin */ 8774887Schin off_t hist_seek(register History_t *hp, int n) 8784887Schin { 8794887Schin return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET)); 8804887Schin } 8814887Schin 8824887Schin /* 8834887Schin * write the command starting at offset <offset> onto file <outfile>. 8844887Schin * if character <last> appears before newline it is deleted 8854887Schin * each new-line character is replaced with string <nl>. 8864887Schin */ 8874887Schin 8884887Schin void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl) 8894887Schin { 8904887Schin register int oldc=0; 8914887Schin register int c; 8924887Schin if(offset<0 || !hp) 8934887Schin { 8944887Schin sfputr(outfile,sh_translate(e_unknown),'\n'); 8954887Schin return; 8964887Schin } 8974887Schin sfseek(hp->histfp,offset,SEEK_SET); 8984887Schin while((c = sfgetc(hp->histfp)) != EOF) 8994887Schin { 9004887Schin if(c && oldc=='\n') 9014887Schin sfputr(outfile,nl,-1); 9024887Schin else if(last && (c==0 || (c=='\n' && oldc==last))) 9034887Schin return; 9044887Schin else if(oldc) 9054887Schin sfputc(outfile,oldc); 9064887Schin oldc = c; 9074887Schin if(c==0) 9084887Schin return; 9094887Schin } 9104887Schin return; 9114887Schin } 9124887Schin 9134887Schin /* 9144887Schin * find index for last line with given string 9154887Schin * If flag==0 then line must begin with string 9164887Schin * direction < 1 for backwards search 9174887Schin */ 9184887Schin 9194887Schin Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction) 9204887Schin { 9214887Schin register int index2; 9224887Schin off_t offset; 9234887Schin int *coffset=0; 9244887Schin Histloc_t location; 9254887Schin location.hist_command = -1; 9264887Schin location.hist_char = 0; 9274887Schin location.hist_line = 0; 9284887Schin if(!hp) 9294887Schin return(location); 9304887Schin /* leading ^ means beginning of line unless escaped */ 9314887Schin if(flag) 9324887Schin { 9334887Schin index2 = *string; 9344887Schin if(index2=='\\') 9354887Schin string++; 9364887Schin else if(index2=='^') 9374887Schin { 9384887Schin flag=0; 9394887Schin string++; 9404887Schin } 9414887Schin } 9424887Schin if(flag) 9434887Schin coffset = &location.hist_char; 9444887Schin index2 = (int)hp->histind; 9454887Schin if(direction<0) 9464887Schin { 9474887Schin index2 -= hp->histsize; 9484887Schin if(index2<1) 9494887Schin index2 = 1; 9504887Schin if(index1 <= index2) 9514887Schin return(location); 9524887Schin } 9534887Schin else if(index1 >= index2) 9544887Schin return(location); 9554887Schin while(index1!=index2) 9564887Schin { 9574887Schin direction>0?++index1:--index1; 9584887Schin offset = hist_tell(hp,index1); 9594887Schin if((location.hist_line=hist_match(hp,offset,string,coffset))>=0) 9604887Schin { 9614887Schin location.hist_command = index1; 9624887Schin return(location); 9634887Schin } 9644887Schin #if KSHELL 9654887Schin /* allow a search to be aborted */ 966*8462SApril.Chin@Sun.COM if(((Shell_t*)hp->histshell)->trapnote&SH_SIGSET) 9674887Schin break; 9684887Schin #endif /* KSHELL */ 9694887Schin } 9704887Schin return(location); 9714887Schin } 9724887Schin 9734887Schin /* 9744887Schin * search for <string> in history file starting at location <offset> 9754887Schin * If coffset==0 then line must begin with string 9764887Schin * returns the line number of the match if successful, otherwise -1 9774887Schin */ 9784887Schin 9794887Schin int hist_match(register History_t *hp,off_t offset,char *string,int *coffset) 9804887Schin { 9814887Schin register unsigned char *first, *cp; 9824887Schin register int m,n,c=1,line=0; 9834887Schin #if SHOPT_MULTIBYTE 9844887Schin mbinit(); 9854887Schin #endif /* SHOPT_MULTIBYTE */ 9864887Schin sfseek(hp->histfp,offset,SEEK_SET); 9874887Schin if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0))) 9884887Schin return(-1); 9894887Schin m = sfvalue(hp->histfp); 9904887Schin n = strlen(string); 9914887Schin while(m > n) 9924887Schin { 9934887Schin if(*cp==*string && memcmp(cp,string,n)==0) 9944887Schin { 9954887Schin if(coffset) 9964887Schin *coffset = (cp-first); 9974887Schin return(line); 9984887Schin } 9994887Schin if(!coffset) 10004887Schin break; 10014887Schin if(*cp=='\n') 10024887Schin line++; 10034887Schin #if SHOPT_MULTIBYTE 10044887Schin if((c=mbsize(cp)) < 0) 10054887Schin c = 1; 10064887Schin #endif /* SHOPT_MULTIBYTE */ 10074887Schin cp += c; 10084887Schin m -= c; 10094887Schin } 10104887Schin return(-1); 10114887Schin } 10124887Schin 10134887Schin 10144887Schin #if SHOPT_ESH || SHOPT_VSH 10154887Schin /* 10164887Schin * copy command <command> from history file to s1 10174887Schin * at most <size> characters copied 10184887Schin * if s1==0 the number of lines for the command is returned 10194887Schin * line=linenumber for emacs copy and only this line of command will be copied 10204887Schin * line < 0 for full command copy 10214887Schin * -1 returned if there is no history file 10224887Schin */ 10234887Schin 10244887Schin int hist_copy(char *s1,int size,int command,int line) 10254887Schin { 10264887Schin register int c; 10274887Schin register History_t *hp = sh_getinterp()->hist_ptr; 10284887Schin register int count = 0; 10294887Schin register char *s1max = s1+size; 10304887Schin if(!hp) 10314887Schin return(-1); 10324887Schin hist_seek(hp,command); 10334887Schin while ((c = sfgetc(hp->histfp)) && c!=EOF) 10344887Schin { 10354887Schin if(c=='\n') 10364887Schin { 10374887Schin if(count++ ==line) 10384887Schin break; 10394887Schin else if(line >= 0) 10404887Schin continue; 10414887Schin } 10424887Schin if(s1 && (line<0 || line==count)) 10434887Schin { 10444887Schin if(s1 >= s1max) 10454887Schin { 10464887Schin *--s1 = 0; 10474887Schin break; 10484887Schin } 10494887Schin *s1++ = c; 10504887Schin } 10514887Schin 10524887Schin } 10534887Schin sfseek(hp->histfp,(off_t)0,SEEK_END); 10544887Schin if(s1==0) 10554887Schin return(count); 10564887Schin if(count && (c= *(s1-1)) == '\n') 10574887Schin s1--; 10584887Schin *s1 = '\0'; 10594887Schin return(count); 10604887Schin } 10614887Schin 10624887Schin /* 10634887Schin * return word number <word> from command number <command> 10644887Schin */ 10654887Schin 10664887Schin char *hist_word(char *string,int size,int word) 10674887Schin { 10684887Schin register int c; 10694887Schin register char *s1 = string; 10704887Schin register unsigned char *cp = (unsigned char*)s1; 10714887Schin register int flag = 0; 10724887Schin History_t *hp = hist_ptr; 10734887Schin if(!hp) 10744887Schin #if KSHELL 10754887Schin { 1076*8462SApril.Chin@Sun.COM strncpy(string,((Shell_t*)hp->histshell)->lastarg,size); 10774887Schin return(string); 10784887Schin } 10794887Schin #else 10804887Schin return(NIL(char*)); 10814887Schin #endif /* KSHELL */ 10824887Schin hist_copy(string,size,(int)hp->histind-1,-1); 10834887Schin for(;c = *cp;cp++) 10844887Schin { 10854887Schin c = isspace(c); 10864887Schin if(c && flag) 10874887Schin { 10884887Schin *cp = 0; 10894887Schin if(--word==0) 10904887Schin break; 10914887Schin flag = 0; 10924887Schin } 10934887Schin else if(c==0 && flag==0) 10944887Schin { 10954887Schin s1 = (char*)cp; 10964887Schin flag++; 10974887Schin } 10984887Schin } 10994887Schin *cp = 0; 11004887Schin if(s1 != string) 11014887Schin strcpy(string,s1); 11024887Schin return(string); 11034887Schin } 11044887Schin 11054887Schin #endif /* SHOPT_ESH */ 11064887Schin 11074887Schin #if SHOPT_ESH 11084887Schin /* 11094887Schin * given the current command and line number, 11104887Schin * and number of lines back or foward, 11114887Schin * compute the new command and line number. 11124887Schin */ 11134887Schin 11144887Schin Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines) 11154887Schin { 11164887Schin Histloc_t next; 11174887Schin line += lines; 11184887Schin if(!hp) 11194887Schin { 11204887Schin command = -1; 11214887Schin goto done; 11224887Schin } 11234887Schin if(lines > 0) 11244887Schin { 11254887Schin register int count; 11264887Schin while(command <= hp->histind) 11274887Schin { 11284887Schin count = hist_copy(NIL(char*),0, command,-1); 11294887Schin if(count > line) 11304887Schin goto done; 11314887Schin line -= count; 11324887Schin command++; 11334887Schin } 11344887Schin } 11354887Schin else 11364887Schin { 11374887Schin register int least = (int)hp->histind-hp->histsize; 11384887Schin while(1) 11394887Schin { 11404887Schin if(line >=0) 11414887Schin goto done; 11424887Schin if(--command < least) 11434887Schin break; 11444887Schin line += hist_copy(NIL(char*),0, command,-1); 11454887Schin } 11464887Schin command = -1; 11474887Schin } 11484887Schin done: 11494887Schin next.hist_line = line; 11504887Schin next.hist_command = command; 11514887Schin return(next); 11524887Schin } 11534887Schin #endif /* SHOPT_ESH */ 11544887Schin 11554887Schin 11564887Schin /* 11574887Schin * Handle history file exceptions 11584887Schin */ 11594887Schin #ifdef SF_BUFCONST 11604887Schin static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle) 11614887Schin #else 11624887Schin static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle) 11634887Schin #endif 11644887Schin { 11654887Schin register int newfd,oldfd; 11664887Schin History_t *hp = (History_t*)handle; 11674887Schin if(type==SF_WRITE) 11684887Schin { 11694887Schin if(errno==ENOSPC || hp->histwfail++ >= 10) 11704887Schin return(0); 11714887Schin /* write failure could be NFS problem, try to re-open */ 11724887Schin close(oldfd=sffileno(fp)); 11734887Schin if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0) 11744887Schin { 11754887Schin if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd) 11764887Schin return(-1); 11774887Schin fcntl(oldfd,F_SETFD,FD_CLOEXEC); 11784887Schin close(newfd); 11794887Schin if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt) 11804887Schin { 11814887Schin register int index = hp->histind; 11824887Schin lseek(oldfd,(off_t)2,SEEK_SET); 11834887Schin hp->histcnt = 2; 11844887Schin hp->histind = 1; 11854887Schin hp->histcmds[1] = 2; 11864887Schin hist_eof(hp); 11874887Schin hp->histmarker = hp->histcnt; 11884887Schin hp->histind = index; 11894887Schin } 11904887Schin return(1); 11914887Schin } 11924887Schin errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname); 11934887Schin return(-1); 11944887Schin } 11954887Schin return(0); 11964887Schin } 1197