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