14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1982-2010 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"
8410898Sroland.mainz@nrubsig.org #else
8510898Sroland.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
acctinit(History_t * hp)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
sh_checkaudit(History_t * hp,const char * name,char * logbuf,size_t len)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
hist_touch(void * handle)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 */
sh_histinit(void * sh_context)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
hist_close(register History_t * hp)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 */
hist_check(register int fd)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 */
hist_clean(int fd)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
hist_trim(History_t * hp,int n)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 */
hist_nearend(History_t * hp,Sfio_t * iop,register off_t size)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
hist_eof(register History_t * hp)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
hist_cancel(register History_t * hp)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
hist_flush(register History_t * hp)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
hist_write(Sfio_t * iop,const void * buff,register size_t insize,Sfdisc_t * handle)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
hist_marker(register char * buff,register long cmdno)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 */
hist_tell(register History_t * hp,int n)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 */
hist_seek(register History_t * hp,int n)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
hist_list(register History_t * hp,Sfio_t * outfile,off_t offset,int last,char * nl)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
hist_find(register History_t * hp,char * string,register int index1,int flag,int direction)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
hist_match(register History_t * hp,off_t offset,char * string,int * coffset)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
hist_copy(char * s1,int size,int command,int line)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
hist_word(char * string,int size,int word)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
hist_locate(History_t * hp,register int command,register int line,int lines)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
hist_exceptf(Sfio_t * fp,int type,void * data,Sfdisc_t * handle)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