14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*10898Sroland.mainz@nrubsig.org * Copyright (c) 1982-2009 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 78462SApril.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 528462SApril.Chin@Sun.COM #if SHOPT_AUDIT 538462SApril.Chin@Sun.COM # define _HIST_AUDIT Sfio_t *auditfp; \ 548462SApril.Chin@Sun.COM char *tty; \ 558462SApril.Chin@Sun.COM int auditmask; 568462SApril.Chin@Sun.COM #else 578462SApril.Chin@Sun.COM # define _HIST_AUDIT 588462SApril.Chin@Sun.COM #endif 598462SApril.Chin@Sun.COM 604887Schin #define _HIST_PRIVATE \ 618462SApril.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; \ 688462SApril.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 <ls.h> 784887Schin #if KSHELL 794887Schin # include "defs.h" 804887Schin # include "variables.h" 814887Schin # include "path.h" 824887Schin # include "builtins.h" 834887Schin # include "io.h" 84*10898Sroland.mainz@nrubsig.org #else 85*10898Sroland.mainz@nrubsig.org # include <ctype.h> 864887Schin #endif /* KSHELL */ 874887Schin #include "history.h" 884887Schin 894887Schin #if !KSHELL 904887Schin # define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x))) 914887Schin # define NIL(type) ((type)0) 924887Schin # define path_relative(x) (x) 934887Schin # ifdef __STDC__ 944887Schin # define nv_getval(s) getenv(#s) 954887Schin # else 964887Schin # define nv_getval(s) getenv("s") 974887Schin # endif /* __STDC__ */ 984887Schin # define e_unknown "unknown" 994887Schin # define sh_translate(x) (x) 1004887Schin char login_sh = 0; 1014887Schin char hist_fname[] = "/.history"; 1024887Schin #endif /* KSHELL */ 1034887Schin 1044887Schin #ifndef O_BINARY 1054887Schin # define O_BINARY 0 1064887Schin #endif /* O_BINARY */ 1074887Schin 1084887Schin int _Hist = 0; 1094887Schin static void hist_marker(char*,long); 1108462SApril.Chin@Sun.COM static History_t* hist_trim(History_t*, int); 1114887Schin static int hist_nearend(History_t*,Sfio_t*, off_t); 1124887Schin static int hist_check(int); 1134887Schin static int hist_clean(int); 1144887Schin #ifdef SF_BUFCONST 1154887Schin static ssize_t hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*); 1164887Schin static int hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*); 1174887Schin #else 1184887Schin static int hist_write(Sfio_t*, const void*, int, Sfdisc_t*); 1194887Schin static int hist_exceptf(Sfio_t*, int, Sfdisc_t*); 1204887Schin #endif 1214887Schin 1224887Schin 1234887Schin static int histinit; 1244887Schin static mode_t histmode; 1254887Schin static History_t *wasopen; 1264887Schin static History_t *hist_ptr; 1274887Schin 1284887Schin #if SHOPT_ACCTFILE 1294887Schin static int acctfd; 1304887Schin static char *logname; 1314887Schin # include <pwd.h> 1324887Schin 1338462SApril.Chin@Sun.COM static int acctinit(History_t *hp) 1344887Schin { 1354887Schin register char *cp, *acctfile; 1368462SApril.Chin@Sun.COM Namval_t *np = nv_search("ACCTFILE",((Shell_t*)hp->histshell)->var_tree,0); 1374887Schin 1384887Schin if(!np || !(acctfile=nv_getval(np))) 1394887Schin return(0); 1404887Schin if(!(cp = getlogin())) 1414887Schin { 1424887Schin struct passwd *userinfo = getpwuid(getuid()); 1434887Schin if(userinfo) 1444887Schin cp = userinfo->pw_name; 1454887Schin else 1464887Schin cp = "unknown"; 1474887Schin } 1484887Schin logname = strdup(cp); 1494887Schin if((acctfd=sh_open(acctfile, 1504887Schin O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && 1514887Schin (unsigned)acctfd < 10) 1524887Schin { 1534887Schin int n; 1544887Schin if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0) 1554887Schin { 1564887Schin close(acctfd); 1574887Schin acctfd = n; 1584887Schin } 1594887Schin } 1604887Schin if(acctfd < 0) 1614887Schin { 1624887Schin acctfd = 0; 1634887Schin return(0); 1644887Schin } 1654887Schin if(strmatch(acctfile,e_devfdNN)) 1664887Schin { 1674887Schin char newfile[16]; 1684887Schin sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd); 1694887Schin nv_putval(np,newfile,NV_RDONLY); 1704887Schin } 1714887Schin else 1724887Schin fcntl(acctfd,F_SETFD,FD_CLOEXEC); 1734887Schin return(1); 1744887Schin } 1754887Schin #endif /* SHOPT_ACCTFILE */ 1764887Schin 1778462SApril.Chin@Sun.COM #if SHOPT_AUDIT 1788462SApril.Chin@Sun.COM static int sh_checkaudit(History_t *hp, const char *name, char *logbuf, size_t len) 1798462SApril.Chin@Sun.COM { 1808462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)hp->histshell; 1818462SApril.Chin@Sun.COM char *buff, *cp, *last; 1828462SApril.Chin@Sun.COM int id1, id2, r=0, n, fd; 1838462SApril.Chin@Sun.COM if((fd=open(name, O_RDONLY)) < 0) 1848462SApril.Chin@Sun.COM return(0); 1858462SApril.Chin@Sun.COM if((n = read(fd, logbuf,len-1)) < 0) 1868462SApril.Chin@Sun.COM goto done; 1878462SApril.Chin@Sun.COM while(logbuf[n-1]=='\n') 1888462SApril.Chin@Sun.COM n--; 1898462SApril.Chin@Sun.COM logbuf[n] = 0; 1908462SApril.Chin@Sun.COM if(!(cp=strchr(logbuf,';')) && !(cp=strchr(logbuf,' '))) 1918462SApril.Chin@Sun.COM goto done; 1928462SApril.Chin@Sun.COM *cp = 0; 1938462SApril.Chin@Sun.COM do 1948462SApril.Chin@Sun.COM { 1958462SApril.Chin@Sun.COM cp++; 1968462SApril.Chin@Sun.COM id1 = id2 = strtol(cp,&last,10); 1978462SApril.Chin@Sun.COM if(*last=='-') 1988462SApril.Chin@Sun.COM id1 = strtol(last+1,&last,10); 1998462SApril.Chin@Sun.COM if(shp->euserid >=id1 && shp->euserid <= id2) 2008462SApril.Chin@Sun.COM r |= 1; 2018462SApril.Chin@Sun.COM if(shp->userid >=id1 && shp->userid <= id2) 2028462SApril.Chin@Sun.COM r |= 2; 2038462SApril.Chin@Sun.COM cp = last; 2048462SApril.Chin@Sun.COM } 2058462SApril.Chin@Sun.COM while(*cp==';' || *cp==' '); 2068462SApril.Chin@Sun.COM done: 2078462SApril.Chin@Sun.COM close(fd); 2088462SApril.Chin@Sun.COM return(r); 2098462SApril.Chin@Sun.COM 2108462SApril.Chin@Sun.COM } 2118462SApril.Chin@Sun.COM #endif /*SHOPT_AUDIT*/ 2128462SApril.Chin@Sun.COM 2134887Schin static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION }; 2144887Schin static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL}; 2154887Schin 2164887Schin static void hist_touch(void *handle) 2174887Schin { 2184887Schin touch((char*)handle, (time_t)0, (time_t)0, 0); 2194887Schin } 2204887Schin 2214887Schin /* 2224887Schin * open the history file 2234887Schin * if HISTNAME is not given and userid==0 then no history file. 2244887Schin * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is 2254887Schin * cleaned up. 2264887Schin * hist_open() returns 1, if history file is open 2274887Schin */ 2288462SApril.Chin@Sun.COM int sh_histinit(void *sh_context) 2294887Schin { 2308462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)sh_context; 2314887Schin register int fd; 2324887Schin register History_t *hp; 2334887Schin register char *histname; 2344887Schin char *fname=0; 2354887Schin int histmask, maxlines, hist_start=0; 2364887Schin register char *cp; 2374887Schin register off_t hsize = 0; 2384887Schin 2398462SApril.Chin@Sun.COM if(shp->hist_ptr=hist_ptr) 2404887Schin return(1); 2414887Schin if(!(histname = nv_getval(HISTFILE))) 2424887Schin { 2434887Schin int offset = staktell(); 2444887Schin if(cp=nv_getval(HOME)) 2454887Schin stakputs(cp); 2464887Schin stakputs(hist_fname); 2474887Schin stakputc(0); 2484887Schin stakseek(offset); 2494887Schin histname = stakptr(offset); 2504887Schin } 2514887Schin #ifdef future 2524887Schin if(hp=wasopen) 2534887Schin { 2544887Schin /* reuse history file if same name */ 2554887Schin wasopen = 0; 2568462SApril.Chin@Sun.COM shp->hist_ptr = hist_ptr = hp; 2574887Schin if(strcmp(histname,hp->histname)==0) 2584887Schin return(1); 2594887Schin else 2604887Schin hist_free(); 2614887Schin } 2624887Schin #endif 2634887Schin retry: 2644887Schin cp = path_relative(histname); 2654887Schin if(!histinit) 2664887Schin histmode = S_IRUSR|S_IWUSR; 2674887Schin if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) 2684887Schin { 2694887Schin hsize=lseek(fd,(off_t)0,SEEK_END); 2704887Schin } 2714887Schin if((unsigned)fd <=2) 2724887Schin { 2734887Schin int n; 2744887Schin if((n=fcntl(fd,F_DUPFD,10))>=0) 2754887Schin { 2764887Schin close(fd); 2774887Schin fd=n; 2784887Schin } 2794887Schin } 2804887Schin /* make sure that file has history file format */ 2814887Schin if(hsize && hist_check(fd)) 2824887Schin { 2834887Schin close(fd); 2844887Schin hsize = 0; 2854887Schin if(unlink(cp)>=0) 2864887Schin goto retry; 2874887Schin fd = -1; 2884887Schin } 2894887Schin if(fd < 0) 2904887Schin { 2914887Schin #if KSHELL 2924887Schin /* don't allow root a history_file in /tmp */ 2938462SApril.Chin@Sun.COM if(shp->userid) 2944887Schin #endif /* KSHELL */ 2954887Schin { 2964887Schin if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) 2974887Schin return(0); 2984887Schin fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); 2994887Schin } 3004887Schin } 3014887Schin if(fd<0) 3024887Schin return(0); 3034887Schin /* set the file to close-on-exec */ 3044887Schin fcntl(fd,F_SETFD,FD_CLOEXEC); 3054887Schin if(cp=nv_getval(HISTSIZE)) 3064887Schin maxlines = (unsigned)strtol(cp, (char**)0, 10); 3074887Schin else 3084887Schin maxlines = HIST_DFLT; 3094887Schin for(histmask=16;histmask <= maxlines; histmask <<=1 ); 3104887Schin if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) 3114887Schin { 3124887Schin close(fd); 3134887Schin return(0); 3144887Schin } 3158462SApril.Chin@Sun.COM shp->hist_ptr = hist_ptr = hp; 3168462SApril.Chin@Sun.COM hp->histshell = (void*)shp; 3174887Schin hp->histsize = maxlines; 3184887Schin hp->histmask = histmask; 3194887Schin hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); 3204887Schin memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); 3214887Schin hp->histind = 1; 3224887Schin hp->histcmds[1] = 2; 3234887Schin hp->histcnt = 2; 3244887Schin hp->histname = strdup(histname); 3254887Schin hp->histdisc = hist_disc; 3264887Schin if(hsize==0) 3274887Schin { 3284887Schin /* put special characters at front of file */ 3294887Schin sfwrite(hp->histfp,(char*)hist_stamp,2); 3304887Schin sfsync(hp->histfp); 3314887Schin } 3324887Schin /* initialize history list */ 3334887Schin else 3344887Schin { 3354887Schin int first,last; 3364887Schin off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; 3374887Schin hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); 3384887Schin hist_eof(hp); /* this sets histind to last command */ 3394887Schin if((hist_start = (last=(int)hp->histind)-maxlines) <=0) 3404887Schin hist_start = 1; 3414887Schin mark = hp->histmarker; 3424887Schin while(first > hist_start) 3434887Schin { 3444887Schin size += size; 3454887Schin first = hist_nearend(hp,hp->histfp,hsize-size); 3464887Schin hp->histind = first; 3474887Schin } 3484887Schin histinit = hist_start; 3494887Schin hist_eof(hp); 3504887Schin if(!histinit) 3514887Schin { 3524887Schin sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); 3534887Schin hp->histind = last; 3544887Schin hp->histmarker = mark; 3554887Schin } 3564887Schin histinit = 0; 3574887Schin } 3584887Schin if(fname) 3594887Schin { 3604887Schin unlink(fname); 3614887Schin free((void*)fname); 3624887Schin } 3634887Schin if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) 3644887Schin { 3654887Schin #ifdef DEBUG 3664887Schin sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); 3674887Schin sfsync(sfstderr); 3684887Schin #endif /* DEBUG */ 3698462SApril.Chin@Sun.COM hp = hist_trim(hp,(int)hp->histind-maxlines); 3704887Schin } 3714887Schin sfdisc(hp->histfp,&hp->histdisc); 3724887Schin #if KSHELL 3734887Schin (HISTCUR)->nvalue.lp = (&hp->histind); 3744887Schin #endif /* KSHELL */ 3754887Schin sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); 3764887Schin #if SHOPT_ACCTFILE 3774887Schin if(sh_isstate(SH_INTERACTIVE)) 3788462SApril.Chin@Sun.COM acctinit(hp); 3794887Schin #endif /* SHOPT_ACCTFILE */ 3808462SApril.Chin@Sun.COM #if SHOPT_AUDIT 3818462SApril.Chin@Sun.COM { 3828462SApril.Chin@Sun.COM char buff[SF_BUFSIZE]; 3838462SApril.Chin@Sun.COM hp->auditfp = 0; 3848462SApril.Chin@Sun.COM if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) 3858462SApril.Chin@Sun.COM { 3868462SApril.Chin@Sun.COM if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) 3878462SApril.Chin@Sun.COM { 3888462SApril.Chin@Sun.COM int n; 3898462SApril.Chin@Sun.COM if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) 3908462SApril.Chin@Sun.COM { 3918462SApril.Chin@Sun.COM sh_close(fd); 3928462SApril.Chin@Sun.COM fd = n; 3938462SApril.Chin@Sun.COM } 3948462SApril.Chin@Sun.COM } 3958462SApril.Chin@Sun.COM if(fd>=0) 3968462SApril.Chin@Sun.COM { 3978462SApril.Chin@Sun.COM hp->tty = strdup(ttyname(2)); 3988462SApril.Chin@Sun.COM hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); 3998462SApril.Chin@Sun.COM } 4008462SApril.Chin@Sun.COM } 4018462SApril.Chin@Sun.COM } 4028462SApril.Chin@Sun.COM #endif 4034887Schin return(1); 4044887Schin } 4054887Schin 4064887Schin /* 4074887Schin * close the history file and free the space 4084887Schin */ 4094887Schin 4104887Schin void hist_close(register History_t *hp) 4114887Schin { 4128462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)hp->histshell; 4134887Schin sfclose(hp->histfp); 4148462SApril.Chin@Sun.COM #if SHOPT_AUDIT 4158462SApril.Chin@Sun.COM if(hp->auditfp) 4168462SApril.Chin@Sun.COM { 4178462SApril.Chin@Sun.COM if(hp->tty) 4188462SApril.Chin@Sun.COM free((void*)hp->tty); 4198462SApril.Chin@Sun.COM sfclose(hp->auditfp); 4208462SApril.Chin@Sun.COM } 4218462SApril.Chin@Sun.COM #endif /* SHOPT_AUDIT */ 4224887Schin free((char*)hp); 4234887Schin hist_ptr = 0; 4248462SApril.Chin@Sun.COM shp->hist_ptr = 0; 4254887Schin #if SHOPT_ACCTFILE 4264887Schin if(acctfd) 4274887Schin { 4284887Schin close(acctfd); 4294887Schin acctfd = 0; 4304887Schin } 4314887Schin #endif /* SHOPT_ACCTFILE */ 4324887Schin } 4334887Schin 4344887Schin /* 4354887Schin * check history file format to see if it begins with special byte 4364887Schin */ 4374887Schin static int hist_check(register int fd) 4384887Schin { 4394887Schin unsigned char magic[2]; 4404887Schin lseek(fd,(off_t)0,SEEK_SET); 4414887Schin if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO)) 4424887Schin return(1); 4434887Schin return(0); 4444887Schin } 4454887Schin 4464887Schin /* 4474887Schin * clean out history file OK if not modified in HIST_RECENT seconds 4484887Schin */ 4494887Schin static int hist_clean(int fd) 4504887Schin { 4514887Schin struct stat statb; 4524887Schin return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT); 4534887Schin } 4544887Schin 4554887Schin /* 4564887Schin * Copy the last <n> commands to a new file and make this the history file 4574887Schin */ 4584887Schin 4598462SApril.Chin@Sun.COM static History_t* hist_trim(History_t *hp, int n) 4604887Schin { 4614887Schin register char *cp; 4624887Schin register int incmd=1, c=0; 4634887Schin register History_t *hist_new, *hist_old = hp; 4644887Schin char *buff, *endbuff, *tmpname=0; 4654887Schin off_t oldp,newp; 4664887Schin struct stat statb; 4674887Schin unlink(hist_old->histname); 4684887Schin if(access(hist_old->histname,F_OK) >= 0) 4694887Schin { 4704887Schin /* The unlink can fail on windows 95 */ 4714887Schin int fd; 4724887Schin char *last, *name=hist_old->histname; 4734887Schin close(sffileno(hist_old->histfp)); 4744887Schin tmpname = (char*)malloc(strlen(name)+14); 4754887Schin if(last = strrchr(name,'/')) 4764887Schin { 4774887Schin *last = 0; 4784887Schin pathtmp(tmpname,name,"hist",NIL(int*)); 4794887Schin *last = '/'; 4804887Schin } 4814887Schin else 4824887Schin pathtmp(tmpname,".","hist",NIL(int*)); 4834887Schin if(rename(name,tmpname) < 0) 4844887Schin tmpname = name; 4854887Schin fd = open(tmpname,O_RDONLY); 4864887Schin sfsetfd(hist_old->histfp,fd); 4874887Schin if(tmpname==name) 4884887Schin tmpname = 0; 4894887Schin } 4908462SApril.Chin@Sun.COM hist_ptr = 0; 4914887Schin if(fstat(sffileno(hist_old->histfp),&statb)>=0) 4924887Schin { 4934887Schin histinit = 1; 4944887Schin histmode = statb.st_mode; 4954887Schin } 4968462SApril.Chin@Sun.COM if(!sh_histinit(hp->histshell)) 4974887Schin { 4984887Schin /* use the old history file */ 4998462SApril.Chin@Sun.COM return hist_ptr = hist_old; 5004887Schin } 5014887Schin hist_new = hist_ptr; 5024887Schin hist_ptr = hist_old; 5034887Schin if(--n < 0) 5044887Schin n = 0; 5054887Schin newp = hist_seek(hist_old,++n); 5064887Schin while(1) 5074887Schin { 5084887Schin if(!incmd) 5094887Schin { 5104887Schin c = hist_ind(hist_new,++hist_new->histind); 5114887Schin hist_new->histcmds[c] = hist_new->histcnt; 5124887Schin if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2) 5134887Schin { 5144887Schin char locbuff[HIST_MARKSZ]; 5154887Schin hist_marker(locbuff,hist_new->histind); 5164887Schin sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ); 5174887Schin hist_new->histcnt += HIST_MARKSZ; 5184887Schin hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt; 5194887Schin } 5204887Schin oldp = newp; 5214887Schin newp = hist_seek(hist_old,++n); 5224887Schin if(newp <=oldp) 5234887Schin break; 5244887Schin } 5254887Schin if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0))) 5264887Schin break; 5274887Schin *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0; 5284887Schin /* copy to null byte */ 5294887Schin incmd = 0; 5304887Schin while(*cp++); 5314887Schin if(cp > endbuff) 5324887Schin incmd = 1; 5334887Schin else if(*cp==0) 5344887Schin cp++; 5354887Schin if(cp > endbuff) 5364887Schin cp = endbuff; 5374887Schin c = cp-buff; 5384887Schin hist_new->histcnt += c; 5394887Schin sfwrite(hist_new->histfp,buff,c); 5404887Schin } 5418462SApril.Chin@Sun.COM hist_cancel(hist_new); 5424887Schin sfclose(hist_old->histfp); 5434887Schin if(tmpname) 5444887Schin { 5454887Schin unlink(tmpname); 5464887Schin free(tmpname); 5474887Schin } 5484887Schin free((char*)hist_old); 5498462SApril.Chin@Sun.COM return hist_ptr = hist_new; 5504887Schin } 5514887Schin 5524887Schin /* 5534887Schin * position history file at size and find next command number 5544887Schin */ 5554887Schin static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size) 5564887Schin { 5574887Schin register unsigned char *cp, *endbuff; 5584887Schin register int n, incmd=1; 5594887Schin unsigned char *buff, marker[4]; 5604887Schin if(size <= 2L || sfseek(iop,size,SEEK_SET)<0) 5614887Schin goto begin; 5624887Schin /* skip to marker command and return the number */ 5634887Schin /* numbering commands occur after a null and begin with HIST_CMDNO */ 5644887Schin while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR)) 5654887Schin { 5664887Schin n = sfvalue(iop); 5674887Schin *(endbuff=cp+n) = 0; 5684887Schin while(1) 5694887Schin { 5704887Schin /* check for marker */ 5714887Schin if(!incmd && *cp++==HIST_CMDNO && *cp==0) 5724887Schin { 5734887Schin n = cp+1 - buff; 5744887Schin incmd = -1; 5754887Schin break; 5764887Schin } 5774887Schin incmd = 0; 5784887Schin while(*cp++); 5794887Schin if(cp>endbuff) 5804887Schin { 5814887Schin incmd = 1; 5824887Schin break; 5834887Schin } 5844887Schin if(*cp==0 && ++cp>endbuff) 5854887Schin break; 5864887Schin } 5874887Schin size += n; 5884887Schin sfread(iop,(char*)buff,n); 5894887Schin if(incmd < 0) 5904887Schin { 5914887Schin if((n=sfread(iop,(char*)marker,4))==4) 5924887Schin { 5934887Schin n = (marker[0]<<16)|(marker[1]<<8)|marker[2]; 5944887Schin if(n < size/2) 5954887Schin { 5964887Schin hp->histmarker = hp->histcnt = size+4; 5974887Schin return(n); 5984887Schin } 5994887Schin n=4; 6004887Schin } 6014887Schin if(n >0) 6024887Schin size += n; 6034887Schin incmd = 0; 6044887Schin } 6054887Schin } 6064887Schin begin: 6074887Schin sfseek(iop,(off_t)2,SEEK_SET); 6084887Schin hp->histmarker = hp->histcnt = 2L; 6094887Schin return(1); 6104887Schin } 6114887Schin 6124887Schin /* 6134887Schin * This routine reads the history file from the present position 6144887Schin * to the end-of-file and puts the information in the in-core 6154887Schin * history table 6164887Schin * Note that HIST_CMDNO is only recognized at the beginning of a command 6174887Schin * and that HIST_UNDO as the first character of a command is skipped 6184887Schin * unless it is followed by 0. If followed by 0 then it cancels 6194887Schin * the previous command. 6204887Schin */ 6214887Schin 6224887Schin void hist_eof(register History_t *hp) 6234887Schin { 6244887Schin register char *cp,*first,*endbuff; 6254887Schin register int incmd = 0; 6264887Schin register off_t count = hp->histcnt; 6274887Schin int n,skip=0; 6284887Schin sfseek(hp->histfp,count,SEEK_SET); 6294887Schin while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0)) 6304887Schin { 6314887Schin n = sfvalue(hp->histfp); 6324887Schin *(endbuff = cp+n) = 0; 6334887Schin first = cp += skip; 6344887Schin while(1) 6354887Schin { 6364887Schin while(!incmd) 6374887Schin { 6384887Schin if(cp>first) 6394887Schin { 6404887Schin count += (cp-first); 6414887Schin n = hist_ind(hp, ++hp->histind); 6424887Schin #ifdef future 6434887Schin if(count==hp->histcmds[n]) 6444887Schin { 6454887Schin sfprintf(sfstderr,"count match n=%d\n",n); 6464887Schin if(histinit) 6474887Schin { 6484887Schin histinit = 0; 6494887Schin return; 6504887Schin } 6514887Schin } 6524887Schin else if(n>=histinit) 6534887Schin #endif 6544887Schin hp->histcmds[n] = count; 6554887Schin first = cp; 6564887Schin } 6574887Schin switch(*((unsigned char*)(cp++))) 6584887Schin { 6594887Schin case HIST_CMDNO: 6604887Schin if(*cp==0) 6614887Schin { 6624887Schin hp->histmarker=count+2; 6634887Schin cp += (HIST_MARKSZ-1); 6644887Schin hp->histind--; 6654887Schin #ifdef future 6664887Schin if(cp <= endbuff) 6674887Schin { 6684887Schin unsigned char *marker = (unsigned char*)(cp-4); 6694887Schin int n = ((marker[0]<<16) 6704887Schin |(marker[1]<<8)|marker[2]); 6714887Schin if((n<count/2) && n != (hp->histind+1)) 6724887Schin errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n); 6734887Schin } 6744887Schin #endif 6754887Schin } 6764887Schin break; 6774887Schin case HIST_UNDO: 6784887Schin if(*cp==0) 6794887Schin { 6804887Schin cp+=1; 6814887Schin hp->histind-=2; 6824887Schin } 6834887Schin break; 6844887Schin default: 6854887Schin cp--; 6864887Schin incmd = 1; 6874887Schin } 6884887Schin if(cp > endbuff) 6894887Schin { 6904887Schin cp++; 6914887Schin goto refill; 6924887Schin } 6934887Schin } 6944887Schin first = cp; 6954887Schin while(*cp++); 6964887Schin if(cp > endbuff) 6974887Schin break; 6984887Schin incmd = 0; 6994887Schin while(*cp==0) 7004887Schin { 7014887Schin if(++cp > endbuff) 7024887Schin goto refill; 7034887Schin } 7044887Schin } 7054887Schin refill: 7064887Schin count += (--cp-first); 7074887Schin skip = (cp-endbuff); 7084887Schin if(!incmd && !skip) 7094887Schin hp->histcmds[hist_ind(hp,++hp->histind)] = count; 7104887Schin } 7114887Schin hp->histcnt = count; 7124887Schin } 7134887Schin 7144887Schin /* 7154887Schin * This routine will cause the previous command to be cancelled 7164887Schin */ 7174887Schin 7184887Schin void hist_cancel(register History_t *hp) 7194887Schin { 7204887Schin register int c; 7214887Schin if(!hp) 7224887Schin return; 7234887Schin sfputc(hp->histfp,HIST_UNDO); 7244887Schin sfputc(hp->histfp,0); 7254887Schin sfsync(hp->histfp); 7264887Schin hp->histcnt += 2; 7274887Schin c = hist_ind(hp,--hp->histind); 7284887Schin hp->histcmds[c] = hp->histcnt; 7294887Schin } 7304887Schin 7314887Schin /* 7324887Schin * flush the current history command 7334887Schin */ 7344887Schin 7354887Schin void hist_flush(register History_t *hp) 7364887Schin { 7374887Schin register char *buff; 7384887Schin if(hp) 7394887Schin { 7404887Schin if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR)) 7414887Schin { 7424887Schin hp->histflush = sfvalue(hp->histfp)+1; 7434887Schin sfwrite(hp->histfp,buff,0); 7444887Schin } 7454887Schin else 7464887Schin hp->histflush=0; 7474887Schin if(sfsync(hp->histfp)<0) 7484887Schin { 7494887Schin hist_close(hp); 7508462SApril.Chin@Sun.COM if(!sh_histinit(hp->histshell)) 7514887Schin sh_offoption(SH_HISTORY); 7524887Schin } 7534887Schin hp->histflush = 0; 7544887Schin } 7554887Schin } 7564887Schin 7574887Schin /* 7584887Schin * This is the write discipline for the history file 7594887Schin * When called from hist_flush(), trailing newlines are deleted and 7604887Schin * a zero byte. Line sequencing is added as required 7614887Schin */ 7624887Schin 7634887Schin #ifdef SF_BUFCONST 7644887Schin static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle) 7654887Schin #else 7664887Schin static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle) 7674887Schin #endif 7684887Schin { 7694887Schin register History_t *hp = (History_t*)handle; 7704887Schin register char *bufptr = ((char*)buff)+insize; 7714887Schin register int c,size = insize; 7724887Schin register off_t cur; 7734887Schin int saved=0; 7744887Schin char saveptr[HIST_MARKSZ]; 7754887Schin if(!hp->histflush) 7764887Schin return(write(sffileno(iop),(char*)buff,size)); 7774887Schin if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0) 7784887Schin { 7794887Schin errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno); 7804887Schin return(-1); 7814887Schin } 7824887Schin hp->histcnt = cur; 7834887Schin /* remove whitespace from end of commands */ 7844887Schin while(--bufptr >= (char*)buff) 7854887Schin { 7864887Schin c= *bufptr; 7874887Schin if(!isspace(c)) 7884887Schin { 7894887Schin if(c=='\\' && *(bufptr+1)!='\n') 7904887Schin bufptr++; 7914887Schin break; 7924887Schin } 7934887Schin } 7944887Schin /* don't count empty lines */ 7954887Schin if(++bufptr <= (char*)buff) 7964887Schin return(insize); 7974887Schin *bufptr++ = '\n'; 7984887Schin *bufptr++ = 0; 7994887Schin size = bufptr - (char*)buff; 8008462SApril.Chin@Sun.COM #if SHOPT_AUDIT 8018462SApril.Chin@Sun.COM if(hp->auditfp) 8028462SApril.Chin@Sun.COM { 8038462SApril.Chin@Sun.COM Shell_t *shp = (Shell_t*)hp->histshell; 8048462SApril.Chin@Sun.COM time_t t=time((time_t*)0); 8058462SApril.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); 8068462SApril.Chin@Sun.COM sfsync(hp->auditfp); 8078462SApril.Chin@Sun.COM } 8088462SApril.Chin@Sun.COM #endif /* SHOPT_AUDIT */ 8094887Schin #if SHOPT_ACCTFILE 8104887Schin if(acctfd) 8114887Schin { 8124887Schin int timechars, offset; 8134887Schin offset = staktell(); 8144887Schin stakputs(buff); 8154887Schin stakseek(staktell() - 1); 8164887Schin timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *))); 8174887Schin lseek(acctfd, (off_t)0, SEEK_END); 8184887Schin write(acctfd, stakptr(offset), size - 2 + timechars); 8194887Schin stakseek(offset); 8204887Schin 8214887Schin } 8224887Schin #endif /* SHOPT_ACCTFILE */ 8234887Schin if(size&01) 8244887Schin { 8254887Schin size++; 8264887Schin *bufptr++ = 0; 8274887Schin } 8284887Schin hp->histcnt += size; 8294887Schin c = hist_ind(hp,++hp->histind); 8304887Schin hp->histcmds[c] = hp->histcnt; 8314887Schin if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2) 8324887Schin { 8334887Schin memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ); 8344887Schin saved=1; 8354887Schin hp->histcnt += HIST_MARKSZ; 8364887Schin hist_marker(bufptr,hp->histind); 8374887Schin hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt; 8384887Schin size += HIST_MARKSZ; 8394887Schin } 8404887Schin errno = 0; 8414887Schin size = write(sffileno(iop),(char*)buff,size); 8424887Schin if(saved) 8434887Schin memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ); 8444887Schin if(size>=0) 8454887Schin { 8464887Schin hp->histwfail = 0; 8474887Schin return(insize); 8484887Schin } 8494887Schin return(-1); 8504887Schin } 8514887Schin 8524887Schin /* 8534887Schin * Put history sequence number <n> into buffer <buff> 8544887Schin * The buffer must be large enough to hold HIST_MARKSZ chars 8554887Schin */ 8564887Schin 8574887Schin static void hist_marker(register char *buff,register long cmdno) 8584887Schin { 8594887Schin *buff++ = HIST_CMDNO; 8604887Schin *buff++ = 0; 8614887Schin *buff++ = (cmdno>>16); 8624887Schin *buff++ = (cmdno>>8); 8634887Schin *buff++ = cmdno; 8644887Schin *buff++ = 0; 8654887Schin } 8664887Schin 8674887Schin /* 8684887Schin * return byte offset in history file for command <n> 8694887Schin */ 8704887Schin off_t hist_tell(register History_t *hp, int n) 8714887Schin { 8724887Schin return(hp->histcmds[hist_ind(hp,n)]); 8734887Schin } 8744887Schin 8754887Schin /* 8764887Schin * seek to the position of command <n> 8774887Schin */ 8784887Schin off_t hist_seek(register History_t *hp, int n) 8794887Schin { 8804887Schin return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET)); 8814887Schin } 8824887Schin 8834887Schin /* 8844887Schin * write the command starting at offset <offset> onto file <outfile>. 8854887Schin * if character <last> appears before newline it is deleted 8864887Schin * each new-line character is replaced with string <nl>. 8874887Schin */ 8884887Schin 8894887Schin void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl) 8904887Schin { 8914887Schin register int oldc=0; 8924887Schin register int c; 8934887Schin if(offset<0 || !hp) 8944887Schin { 8954887Schin sfputr(outfile,sh_translate(e_unknown),'\n'); 8964887Schin return; 8974887Schin } 8984887Schin sfseek(hp->histfp,offset,SEEK_SET); 8994887Schin while((c = sfgetc(hp->histfp)) != EOF) 9004887Schin { 9014887Schin if(c && oldc=='\n') 9024887Schin sfputr(outfile,nl,-1); 9034887Schin else if(last && (c==0 || (c=='\n' && oldc==last))) 9044887Schin return; 9054887Schin else if(oldc) 9064887Schin sfputc(outfile,oldc); 9074887Schin oldc = c; 9084887Schin if(c==0) 9094887Schin return; 9104887Schin } 9114887Schin return; 9124887Schin } 9134887Schin 9144887Schin /* 9154887Schin * find index for last line with given string 9164887Schin * If flag==0 then line must begin with string 9174887Schin * direction < 1 for backwards search 9184887Schin */ 9194887Schin 9204887Schin Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction) 9214887Schin { 9224887Schin register int index2; 9234887Schin off_t offset; 9244887Schin int *coffset=0; 9254887Schin Histloc_t location; 9264887Schin location.hist_command = -1; 9274887Schin location.hist_char = 0; 9284887Schin location.hist_line = 0; 9294887Schin if(!hp) 9304887Schin return(location); 9314887Schin /* leading ^ means beginning of line unless escaped */ 9324887Schin if(flag) 9334887Schin { 9344887Schin index2 = *string; 9354887Schin if(index2=='\\') 9364887Schin string++; 9374887Schin else if(index2=='^') 9384887Schin { 9394887Schin flag=0; 9404887Schin string++; 9414887Schin } 9424887Schin } 9434887Schin if(flag) 9444887Schin coffset = &location.hist_char; 9454887Schin index2 = (int)hp->histind; 9464887Schin if(direction<0) 9474887Schin { 9484887Schin index2 -= hp->histsize; 9494887Schin if(index2<1) 9504887Schin index2 = 1; 9514887Schin if(index1 <= index2) 9524887Schin return(location); 9534887Schin } 9544887Schin else if(index1 >= index2) 9554887Schin return(location); 9564887Schin while(index1!=index2) 9574887Schin { 9584887Schin direction>0?++index1:--index1; 9594887Schin offset = hist_tell(hp,index1); 9604887Schin if((location.hist_line=hist_match(hp,offset,string,coffset))>=0) 9614887Schin { 9624887Schin location.hist_command = index1; 9634887Schin return(location); 9644887Schin } 9654887Schin #if KSHELL 9664887Schin /* allow a search to be aborted */ 9678462SApril.Chin@Sun.COM if(((Shell_t*)hp->histshell)->trapnote&SH_SIGSET) 9684887Schin break; 9694887Schin #endif /* KSHELL */ 9704887Schin } 9714887Schin return(location); 9724887Schin } 9734887Schin 9744887Schin /* 9754887Schin * search for <string> in history file starting at location <offset> 9764887Schin * If coffset==0 then line must begin with string 9774887Schin * returns the line number of the match if successful, otherwise -1 9784887Schin */ 9794887Schin 9804887Schin int hist_match(register History_t *hp,off_t offset,char *string,int *coffset) 9814887Schin { 9824887Schin register unsigned char *first, *cp; 9834887Schin register int m,n,c=1,line=0; 9844887Schin #if SHOPT_MULTIBYTE 9854887Schin mbinit(); 9864887Schin #endif /* SHOPT_MULTIBYTE */ 9874887Schin sfseek(hp->histfp,offset,SEEK_SET); 9884887Schin if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0))) 9894887Schin return(-1); 9904887Schin m = sfvalue(hp->histfp); 9914887Schin n = strlen(string); 9924887Schin while(m > n) 9934887Schin { 9944887Schin if(*cp==*string && memcmp(cp,string,n)==0) 9954887Schin { 9964887Schin if(coffset) 9974887Schin *coffset = (cp-first); 9984887Schin return(line); 9994887Schin } 10004887Schin if(!coffset) 10014887Schin break; 10024887Schin if(*cp=='\n') 10034887Schin line++; 10044887Schin #if SHOPT_MULTIBYTE 10054887Schin if((c=mbsize(cp)) < 0) 10064887Schin c = 1; 10074887Schin #endif /* SHOPT_MULTIBYTE */ 10084887Schin cp += c; 10094887Schin m -= c; 10104887Schin } 10114887Schin return(-1); 10124887Schin } 10134887Schin 10144887Schin 10154887Schin #if SHOPT_ESH || SHOPT_VSH 10164887Schin /* 10174887Schin * copy command <command> from history file to s1 10184887Schin * at most <size> characters copied 10194887Schin * if s1==0 the number of lines for the command is returned 10204887Schin * line=linenumber for emacs copy and only this line of command will be copied 10214887Schin * line < 0 for full command copy 10224887Schin * -1 returned if there is no history file 10234887Schin */ 10244887Schin 10254887Schin int hist_copy(char *s1,int size,int command,int line) 10264887Schin { 10274887Schin register int c; 10284887Schin register History_t *hp = sh_getinterp()->hist_ptr; 10294887Schin register int count = 0; 10304887Schin register char *s1max = s1+size; 10314887Schin if(!hp) 10324887Schin return(-1); 10334887Schin hist_seek(hp,command); 10344887Schin while ((c = sfgetc(hp->histfp)) && c!=EOF) 10354887Schin { 10364887Schin if(c=='\n') 10374887Schin { 10384887Schin if(count++ ==line) 10394887Schin break; 10404887Schin else if(line >= 0) 10414887Schin continue; 10424887Schin } 10434887Schin if(s1 && (line<0 || line==count)) 10444887Schin { 10454887Schin if(s1 >= s1max) 10464887Schin { 10474887Schin *--s1 = 0; 10484887Schin break; 10494887Schin } 10504887Schin *s1++ = c; 10514887Schin } 10524887Schin 10534887Schin } 10544887Schin sfseek(hp->histfp,(off_t)0,SEEK_END); 10554887Schin if(s1==0) 10564887Schin return(count); 10574887Schin if(count && (c= *(s1-1)) == '\n') 10584887Schin s1--; 10594887Schin *s1 = '\0'; 10604887Schin return(count); 10614887Schin } 10624887Schin 10634887Schin /* 10644887Schin * return word number <word> from command number <command> 10654887Schin */ 10664887Schin 10674887Schin char *hist_word(char *string,int size,int word) 10684887Schin { 10694887Schin register int c; 10704887Schin register char *s1 = string; 10714887Schin register unsigned char *cp = (unsigned char*)s1; 10724887Schin register int flag = 0; 10734887Schin History_t *hp = hist_ptr; 10744887Schin if(!hp) 10754887Schin return(NIL(char*)); 10764887Schin hist_copy(string,size,(int)hp->histind-1,-1); 10774887Schin for(;c = *cp;cp++) 10784887Schin { 10794887Schin c = isspace(c); 10804887Schin if(c && flag) 10814887Schin { 10824887Schin *cp = 0; 10834887Schin if(--word==0) 10844887Schin break; 10854887Schin flag = 0; 10864887Schin } 10874887Schin else if(c==0 && flag==0) 10884887Schin { 10894887Schin s1 = (char*)cp; 10904887Schin flag++; 10914887Schin } 10924887Schin } 10934887Schin *cp = 0; 10944887Schin if(s1 != string) 10954887Schin strcpy(string,s1); 10964887Schin return(string); 10974887Schin } 10984887Schin 10994887Schin #endif /* SHOPT_ESH */ 11004887Schin 11014887Schin #if SHOPT_ESH 11024887Schin /* 11034887Schin * given the current command and line number, 11044887Schin * and number of lines back or foward, 11054887Schin * compute the new command and line number. 11064887Schin */ 11074887Schin 11084887Schin Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines) 11094887Schin { 11104887Schin Histloc_t next; 11114887Schin line += lines; 11124887Schin if(!hp) 11134887Schin { 11144887Schin command = -1; 11154887Schin goto done; 11164887Schin } 11174887Schin if(lines > 0) 11184887Schin { 11194887Schin register int count; 11204887Schin while(command <= hp->histind) 11214887Schin { 11224887Schin count = hist_copy(NIL(char*),0, command,-1); 11234887Schin if(count > line) 11244887Schin goto done; 11254887Schin line -= count; 11264887Schin command++; 11274887Schin } 11284887Schin } 11294887Schin else 11304887Schin { 11314887Schin register int least = (int)hp->histind-hp->histsize; 11324887Schin while(1) 11334887Schin { 11344887Schin if(line >=0) 11354887Schin goto done; 11364887Schin if(--command < least) 11374887Schin break; 11384887Schin line += hist_copy(NIL(char*),0, command,-1); 11394887Schin } 11404887Schin command = -1; 11414887Schin } 11424887Schin done: 11434887Schin next.hist_line = line; 11444887Schin next.hist_command = command; 11454887Schin return(next); 11464887Schin } 11474887Schin #endif /* SHOPT_ESH */ 11484887Schin 11494887Schin 11504887Schin /* 11514887Schin * Handle history file exceptions 11524887Schin */ 11534887Schin #ifdef SF_BUFCONST 11544887Schin static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle) 11554887Schin #else 11564887Schin static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle) 11574887Schin #endif 11584887Schin { 11594887Schin register int newfd,oldfd; 11604887Schin History_t *hp = (History_t*)handle; 11614887Schin if(type==SF_WRITE) 11624887Schin { 11634887Schin if(errno==ENOSPC || hp->histwfail++ >= 10) 11644887Schin return(0); 11654887Schin /* write failure could be NFS problem, try to re-open */ 11664887Schin close(oldfd=sffileno(fp)); 11674887Schin if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0) 11684887Schin { 11694887Schin if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd) 11704887Schin return(-1); 11714887Schin fcntl(oldfd,F_SETFD,FD_CLOEXEC); 11724887Schin close(newfd); 11734887Schin if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt) 11744887Schin { 11754887Schin register int index = hp->histind; 11764887Schin lseek(oldfd,(off_t)2,SEEK_SET); 11774887Schin hp->histcnt = 2; 11784887Schin hp->histind = 1; 11794887Schin hp->histcmds[1] = 2; 11804887Schin hist_eof(hp); 11814887Schin hp->histmarker = hp->histcnt; 11824887Schin hp->histind = index; 11834887Schin } 11844887Schin return(1); 11854887Schin } 11864887Schin errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname); 11874887Schin return(-1); 11884887Schin } 11894887Schin return(0); 11904887Schin } 1191