1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                  David Korn <dgk@research.att.com>                   *
18*4887Schin *                                                                      *
19*4887Schin ***********************************************************************/
20*4887Schin #pragma prototyped
21*4887Schin /*
22*4887Schin  *   History file manipulation routines
23*4887Schin  *
24*4887Schin  *   David Korn
25*4887Schin  *   AT&T Labs
26*4887Schin  *
27*4887Schin  */
28*4887Schin 
29*4887Schin /*
30*4887Schin  * Each command in the history file starts on an even byte is null terminated.
31*4887Schin  * The first byte must contain the special character HIST_UNDO and the second
32*4887Schin  * byte is the version number.  The sequence HIST_UNDO 0, following a command,
33*4887Schin  * nullifies the previous command. A six byte sequence starting with
34*4887Schin  * HIST_CMDNO is used to store the command number so that it is not necessary
35*4887Schin  * to read the file from beginning to end to get to the last block of
36*4887Schin  * commands.  This format of this sequence is different in version 1
37*4887Schin  * then in version 0.  Version 1 allows commands to use the full 8 bit
38*4887Schin  * character set.  It can understand version 0 format files.
39*4887Schin  */
40*4887Schin 
41*4887Schin 
42*4887Schin #define HIST_MAX	(sizeof(int)*HIST_BSIZE)
43*4887Schin #define HIST_BIG	(0100000-1024)	/* 1K less than maximum short */
44*4887Schin #define HIST_LINE	32		/* typical length for history line */
45*4887Schin #define HIST_MARKSZ	6
46*4887Schin #define HIST_RECENT	600
47*4887Schin #define HIST_UNDO	0201		/* invalidate previous command */
48*4887Schin #define HIST_CMDNO	0202		/* next 3 bytes give command number */
49*4887Schin #define HIST_BSIZE	4096		/* size of history file buffer */
50*4887Schin #define HIST_DFLT	512		/* default size of history list */
51*4887Schin 
52*4887Schin #define _HIST_PRIVATE \
53*4887Schin 	off_t	histcnt;	/* offset into history file */\
54*4887Schin 	off_t	histmarker;	/* offset of last command marker */ \
55*4887Schin 	int	histflush;	/* set if flushed outside of hflush() */\
56*4887Schin 	int	histmask;	/* power of two mask for histcnt */ \
57*4887Schin 	char	histbuff[HIST_BSIZE+1];	/* history file buffer */ \
58*4887Schin 	int	histwfail; \
59*4887Schin 	off_t	histcmds[2];	/* offset for recent commands, must be last */
60*4887Schin 
61*4887Schin #define hist_ind(hp,c)	((int)((c)&(hp)->histmask))
62*4887Schin 
63*4887Schin #include	<ast.h>
64*4887Schin #include	<sfio.h>
65*4887Schin #include	"FEATURE/time"
66*4887Schin #include	<error.h>
67*4887Schin #include	<ctype.h>
68*4887Schin #include	<ls.h>
69*4887Schin #if KSHELL
70*4887Schin #   include	"defs.h"
71*4887Schin #   include	"variables.h"
72*4887Schin #   include	"path.h"
73*4887Schin #   include	"builtins.h"
74*4887Schin #   include	"io.h"
75*4887Schin #endif	/* KSHELL */
76*4887Schin #include	"history.h"
77*4887Schin 
78*4887Schin #if !KSHELL
79*4887Schin #   define new_of(type,x)	((type*)malloc((unsigned)sizeof(type)+(x)))
80*4887Schin #   define NIL(type)		((type)0)
81*4887Schin #   define path_relative(x)	(x)
82*4887Schin #   ifdef __STDC__
83*4887Schin #	define nv_getval(s)	getenv(#s)
84*4887Schin #   else
85*4887Schin #	define nv_getval(s)	getenv("s")
86*4887Schin #   endif /* __STDC__ */
87*4887Schin #   define e_unknown	 	"unknown"
88*4887Schin #   define sh_translate(x)	(x)
89*4887Schin     char login_sh =		0;
90*4887Schin     char hist_fname[] =		"/.history";
91*4887Schin #endif	/* KSHELL */
92*4887Schin 
93*4887Schin #ifndef O_BINARY
94*4887Schin #   define O_BINARY	0
95*4887Schin #endif /* O_BINARY */
96*4887Schin 
97*4887Schin int	_Hist = 0;
98*4887Schin static void	hist_marker(char*,long);
99*4887Schin static void	hist_trim(History_t*, int);
100*4887Schin static int	hist_nearend(History_t*,Sfio_t*, off_t);
101*4887Schin static int	hist_check(int);
102*4887Schin static int	hist_clean(int);
103*4887Schin #ifdef SF_BUFCONST
104*4887Schin     static ssize_t  hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*);
105*4887Schin     static int      hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*);
106*4887Schin #else
107*4887Schin     static int	hist_write(Sfio_t*, const void*, int, Sfdisc_t*);
108*4887Schin     static int	hist_exceptf(Sfio_t*, int, Sfdisc_t*);
109*4887Schin #endif
110*4887Schin 
111*4887Schin 
112*4887Schin static int	histinit;
113*4887Schin static mode_t	histmode;
114*4887Schin static History_t *wasopen;
115*4887Schin static History_t *hist_ptr;
116*4887Schin 
117*4887Schin #if SHOPT_ACCTFILE
118*4887Schin     static int	acctfd;
119*4887Schin     static char *logname;
120*4887Schin #   include <pwd.h>
121*4887Schin 
122*4887Schin     int  acctinit(void)
123*4887Schin     {
124*4887Schin 	register char *cp, *acctfile;
125*4887Schin 	Namval_t *np = nv_search("ACCTFILE",sh.var_tree,0);
126*4887Schin 
127*4887Schin 	if(!np || !(acctfile=nv_getval(np)))
128*4887Schin 		return(0);
129*4887Schin 	if(!(cp = getlogin()))
130*4887Schin 	{
131*4887Schin 		struct passwd *userinfo = getpwuid(getuid());
132*4887Schin 		if(userinfo)
133*4887Schin 			cp = userinfo->pw_name;
134*4887Schin 		else
135*4887Schin 			cp = "unknown";
136*4887Schin 	}
137*4887Schin 	logname = strdup(cp);
138*4887Schin 
139*4887Schin 	if((acctfd=sh_open(acctfile,
140*4887Schin 		O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 &&
141*4887Schin 	    (unsigned)acctfd < 10)
142*4887Schin 	{
143*4887Schin 		int n;
144*4887Schin 		if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0)
145*4887Schin 		{
146*4887Schin 			close(acctfd);
147*4887Schin 			acctfd = n;
148*4887Schin 		}
149*4887Schin 	}
150*4887Schin 	if(acctfd < 0)
151*4887Schin 	{
152*4887Schin 		acctfd = 0;
153*4887Schin 		return(0);
154*4887Schin 	}
155*4887Schin 	if(strmatch(acctfile,e_devfdNN))
156*4887Schin 	{
157*4887Schin 		char newfile[16];
158*4887Schin 		sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd);
159*4887Schin 		nv_putval(np,newfile,NV_RDONLY);
160*4887Schin 	}
161*4887Schin 	else
162*4887Schin 		fcntl(acctfd,F_SETFD,FD_CLOEXEC);
163*4887Schin 	return(1);
164*4887Schin     }
165*4887Schin #endif /* SHOPT_ACCTFILE */
166*4887Schin 
167*4887Schin static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION };
168*4887Schin static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL};
169*4887Schin 
170*4887Schin static void hist_touch(void *handle)
171*4887Schin {
172*4887Schin 	touch((char*)handle, (time_t)0, (time_t)0, 0);
173*4887Schin }
174*4887Schin 
175*4887Schin /*
176*4887Schin  * open the history file
177*4887Schin  * if HISTNAME is not given and userid==0 then no history file.
178*4887Schin  * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is
179*4887Schin  * cleaned up.
180*4887Schin  * hist_open() returns 1, if history file is open
181*4887Schin  */
182*4887Schin int  sh_histinit(void)
183*4887Schin {
184*4887Schin 	register int fd;
185*4887Schin 	register History_t *hp;
186*4887Schin 	register char *histname;
187*4887Schin 	char *fname=0;
188*4887Schin 	int histmask, maxlines, hist_start=0;
189*4887Schin 	register char *cp;
190*4887Schin 	register off_t hsize = 0;
191*4887Schin 
192*4887Schin 	if(sh.hist_ptr=hist_ptr)
193*4887Schin 		return(1);
194*4887Schin 	if(!(histname = nv_getval(HISTFILE)))
195*4887Schin 	{
196*4887Schin 		int offset = staktell();
197*4887Schin 		if(cp=nv_getval(HOME))
198*4887Schin 			stakputs(cp);
199*4887Schin 		stakputs(hist_fname);
200*4887Schin 		stakputc(0);
201*4887Schin 		stakseek(offset);
202*4887Schin 		histname = stakptr(offset);
203*4887Schin 	}
204*4887Schin #ifdef future
205*4887Schin 	if(hp=wasopen)
206*4887Schin 	{
207*4887Schin 		/* reuse history file if same name */
208*4887Schin 		wasopen = 0;
209*4887Schin 		sh.hist_ptr = hist_ptr = hp;
210*4887Schin 		if(strcmp(histname,hp->histname)==0)
211*4887Schin 			return(1);
212*4887Schin 		else
213*4887Schin 			hist_free();
214*4887Schin 	}
215*4887Schin #endif
216*4887Schin retry:
217*4887Schin 	cp = path_relative(histname);
218*4887Schin 	if(!histinit)
219*4887Schin 		histmode = S_IRUSR|S_IWUSR;
220*4887Schin 	if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0)
221*4887Schin 	{
222*4887Schin 		hsize=lseek(fd,(off_t)0,SEEK_END);
223*4887Schin 	}
224*4887Schin 	if((unsigned)fd <=2)
225*4887Schin 	{
226*4887Schin 		int n;
227*4887Schin 		if((n=fcntl(fd,F_DUPFD,10))>=0)
228*4887Schin 		{
229*4887Schin 			close(fd);
230*4887Schin 			fd=n;
231*4887Schin 		}
232*4887Schin 	}
233*4887Schin 	/* make sure that file has history file format */
234*4887Schin 	if(hsize && hist_check(fd))
235*4887Schin 	{
236*4887Schin 		close(fd);
237*4887Schin 		hsize = 0;
238*4887Schin 		if(unlink(cp)>=0)
239*4887Schin 			goto retry;
240*4887Schin 		fd = -1;
241*4887Schin 	}
242*4887Schin 	if(fd < 0)
243*4887Schin 	{
244*4887Schin #if KSHELL
245*4887Schin 		/* don't allow root a history_file in /tmp */
246*4887Schin 		if(sh.userid)
247*4887Schin #endif	/* KSHELL */
248*4887Schin 		{
249*4887Schin 			if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*))))
250*4887Schin 				return(0);
251*4887Schin 			fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
252*4887Schin 		}
253*4887Schin 	}
254*4887Schin 	if(fd<0)
255*4887Schin 		return(0);
256*4887Schin 	/* set the file to close-on-exec */
257*4887Schin 	fcntl(fd,F_SETFD,FD_CLOEXEC);
258*4887Schin 	if(cp=nv_getval(HISTSIZE))
259*4887Schin 		maxlines = (unsigned)strtol(cp, (char**)0, 10);
260*4887Schin 	else
261*4887Schin 		maxlines = HIST_DFLT;
262*4887Schin 	for(histmask=16;histmask <= maxlines; histmask <<=1 );
263*4887Schin 	if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t))))
264*4887Schin 	{
265*4887Schin 		close(fd);
266*4887Schin 		return(0);
267*4887Schin 	}
268*4887Schin 	sh.hist_ptr = hist_ptr = hp;
269*4887Schin 	hp->histsize = maxlines;
270*4887Schin 	hp->histmask = histmask;
271*4887Schin 	hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE);
272*4887Schin 	memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1));
273*4887Schin 	hp->histind = 1;
274*4887Schin 	hp->histcmds[1] = 2;
275*4887Schin 	hp->histcnt = 2;
276*4887Schin 	hp->histname = strdup(histname);
277*4887Schin 	hp->histdisc = hist_disc;
278*4887Schin 	if(hsize==0)
279*4887Schin 	{
280*4887Schin 		/* put special characters at front of file */
281*4887Schin 		sfwrite(hp->histfp,(char*)hist_stamp,2);
282*4887Schin 		sfsync(hp->histfp);
283*4887Schin 	}
284*4887Schin 	/* initialize history list */
285*4887Schin 	else
286*4887Schin 	{
287*4887Schin 		int first,last;
288*4887Schin 		off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE;
289*4887Schin 		hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size);
290*4887Schin 		hist_eof(hp);	 /* this sets histind to last command */
291*4887Schin 		if((hist_start = (last=(int)hp->histind)-maxlines) <=0)
292*4887Schin 			hist_start = 1;
293*4887Schin 		mark = hp->histmarker;
294*4887Schin 		while(first > hist_start)
295*4887Schin 		{
296*4887Schin 			size += size;
297*4887Schin 			first = hist_nearend(hp,hp->histfp,hsize-size);
298*4887Schin 			hp->histind = first;
299*4887Schin 		}
300*4887Schin 		histinit = hist_start;
301*4887Schin 		hist_eof(hp);
302*4887Schin 		if(!histinit)
303*4887Schin 		{
304*4887Schin 			sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET);
305*4887Schin 			hp->histind = last;
306*4887Schin 			hp->histmarker = mark;
307*4887Schin 		}
308*4887Schin 		histinit = 0;
309*4887Schin 	}
310*4887Schin 	if(fname)
311*4887Schin 	{
312*4887Schin 		unlink(fname);
313*4887Schin 		free((void*)fname);
314*4887Schin 	}
315*4887Schin 	if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX)
316*4887Schin 	{
317*4887Schin #ifdef DEBUG
318*4887Schin 		sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize);
319*4887Schin 		sfsync(sfstderr);
320*4887Schin #endif /* DEBUG */
321*4887Schin 		hist_trim(hp,(int)hp->histind-maxlines);
322*4887Schin 	}
323*4887Schin 	sfdisc(hp->histfp,&hp->histdisc);
324*4887Schin #if KSHELL
325*4887Schin 	(HISTCUR)->nvalue.lp = (&hp->histind);
326*4887Schin #endif /* KSHELL */
327*4887Schin 	sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname);
328*4887Schin #if SHOPT_ACCTFILE
329*4887Schin 	if(sh_isstate(SH_INTERACTIVE))
330*4887Schin 		acctinit();
331*4887Schin #endif /* SHOPT_ACCTFILE */
332*4887Schin 	return(1);
333*4887Schin }
334*4887Schin 
335*4887Schin /*
336*4887Schin  * close the history file and free the space
337*4887Schin  */
338*4887Schin 
339*4887Schin void hist_close(register History_t *hp)
340*4887Schin {
341*4887Schin 	sfclose(hp->histfp);
342*4887Schin 	free((char*)hp);
343*4887Schin 	hist_ptr = 0;
344*4887Schin 	sh.hist_ptr = 0;
345*4887Schin #if SHOPT_ACCTFILE
346*4887Schin 	if(acctfd)
347*4887Schin 	{
348*4887Schin 		close(acctfd);
349*4887Schin 		acctfd = 0;
350*4887Schin 	}
351*4887Schin #endif /* SHOPT_ACCTFILE */
352*4887Schin }
353*4887Schin 
354*4887Schin /*
355*4887Schin  * check history file format to see if it begins with special byte
356*4887Schin  */
357*4887Schin static int hist_check(register int fd)
358*4887Schin {
359*4887Schin 	unsigned char magic[2];
360*4887Schin 	lseek(fd,(off_t)0,SEEK_SET);
361*4887Schin 	if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO))
362*4887Schin 		return(1);
363*4887Schin 	return(0);
364*4887Schin }
365*4887Schin 
366*4887Schin /*
367*4887Schin  * clean out history file OK if not modified in HIST_RECENT seconds
368*4887Schin  */
369*4887Schin static int hist_clean(int fd)
370*4887Schin {
371*4887Schin 	struct stat statb;
372*4887Schin 	return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT);
373*4887Schin }
374*4887Schin 
375*4887Schin /*
376*4887Schin  * Copy the last <n> commands to a new file and make this the history file
377*4887Schin  */
378*4887Schin 
379*4887Schin static void hist_trim(History_t *hp, int n)
380*4887Schin {
381*4887Schin 	register char *cp;
382*4887Schin 	register int incmd=1, c=0;
383*4887Schin 	register History_t *hist_new, *hist_old = hp;
384*4887Schin 	char *buff, *endbuff, *tmpname=0;
385*4887Schin 	off_t oldp,newp;
386*4887Schin 	struct stat statb;
387*4887Schin 	unlink(hist_old->histname);
388*4887Schin 	if(access(hist_old->histname,F_OK) >= 0)
389*4887Schin 	{
390*4887Schin 		/* The unlink can fail on windows 95 */
391*4887Schin 		int fd;
392*4887Schin 		char *last, *name=hist_old->histname;
393*4887Schin 		close(sffileno(hist_old->histfp));
394*4887Schin 		tmpname = (char*)malloc(strlen(name)+14);
395*4887Schin 		if(last = strrchr(name,'/'))
396*4887Schin 		{
397*4887Schin 			*last = 0;
398*4887Schin 			pathtmp(tmpname,name,"hist",NIL(int*));
399*4887Schin 			*last = '/';
400*4887Schin 		}
401*4887Schin 		else
402*4887Schin 			pathtmp(tmpname,".","hist",NIL(int*));
403*4887Schin 		if(rename(name,tmpname) < 0)
404*4887Schin 			tmpname = name;
405*4887Schin 		fd = open(tmpname,O_RDONLY);
406*4887Schin 		sfsetfd(hist_old->histfp,fd);
407*4887Schin 		if(tmpname==name)
408*4887Schin 			tmpname = 0;
409*4887Schin 	}
410*4887Schin 	hp = hist_ptr = 0;
411*4887Schin 	if(fstat(sffileno(hist_old->histfp),&statb)>=0)
412*4887Schin 	{
413*4887Schin 		histinit = 1;
414*4887Schin 		histmode =  statb.st_mode;
415*4887Schin 	}
416*4887Schin 	if(!sh_histinit())
417*4887Schin 	{
418*4887Schin 		/* use the old history file */
419*4887Schin 		hist_ptr = hist_old;
420*4887Schin 		return;
421*4887Schin 	}
422*4887Schin 	hist_new = hist_ptr;
423*4887Schin 	hist_ptr = hist_old;
424*4887Schin 	if(--n < 0)
425*4887Schin 		n = 0;
426*4887Schin 	newp = hist_seek(hist_old,++n);
427*4887Schin 	while(1)
428*4887Schin 	{
429*4887Schin 		if(!incmd)
430*4887Schin 		{
431*4887Schin 			c = hist_ind(hist_new,++hist_new->histind);
432*4887Schin 			hist_new->histcmds[c] = hist_new->histcnt;
433*4887Schin 			if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2)
434*4887Schin 			{
435*4887Schin 				char locbuff[HIST_MARKSZ];
436*4887Schin 				hist_marker(locbuff,hist_new->histind);
437*4887Schin 				sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ);
438*4887Schin 				hist_new->histcnt += HIST_MARKSZ;
439*4887Schin 				hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt;
440*4887Schin 			}
441*4887Schin 			oldp = newp;
442*4887Schin 			newp = hist_seek(hist_old,++n);
443*4887Schin 			if(newp <=oldp)
444*4887Schin 				break;
445*4887Schin 		}
446*4887Schin 		if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0)))
447*4887Schin 			break;
448*4887Schin 		*(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0;
449*4887Schin 		/* copy to null byte */
450*4887Schin 		incmd = 0;
451*4887Schin 		while(*cp++);
452*4887Schin 		if(cp > endbuff)
453*4887Schin 			incmd = 1;
454*4887Schin 		else if(*cp==0)
455*4887Schin 			cp++;
456*4887Schin 		if(cp > endbuff)
457*4887Schin 			cp = endbuff;
458*4887Schin 		c = cp-buff;
459*4887Schin 		hist_new->histcnt += c;
460*4887Schin 		sfwrite(hist_new->histfp,buff,c);
461*4887Schin 	}
462*4887Schin 	hist_ptr = hist_new;
463*4887Schin 	hist_cancel(hist_ptr);
464*4887Schin 	sfclose(hist_old->histfp);
465*4887Schin 	if(tmpname)
466*4887Schin 	{
467*4887Schin 		unlink(tmpname);
468*4887Schin 		free(tmpname);
469*4887Schin 	}
470*4887Schin 	free((char*)hist_old);
471*4887Schin }
472*4887Schin 
473*4887Schin /*
474*4887Schin  * position history file at size and find next command number
475*4887Schin  */
476*4887Schin static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size)
477*4887Schin {
478*4887Schin         register unsigned char *cp, *endbuff;
479*4887Schin         register int n, incmd=1;
480*4887Schin         unsigned char *buff, marker[4];
481*4887Schin 	if(size <= 2L || sfseek(iop,size,SEEK_SET)<0)
482*4887Schin 		goto begin;
483*4887Schin 	/* skip to marker command and return the number */
484*4887Schin 	/* numbering commands occur after a null and begin with HIST_CMDNO */
485*4887Schin         while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR))
486*4887Schin         {
487*4887Schin 		n = sfvalue(iop);
488*4887Schin                 *(endbuff=cp+n) = 0;
489*4887Schin                 while(1)
490*4887Schin                 {
491*4887Schin 			/* check for marker */
492*4887Schin                         if(!incmd && *cp++==HIST_CMDNO && *cp==0)
493*4887Schin                         {
494*4887Schin                                 n = cp+1 - buff;
495*4887Schin                                 incmd = -1;
496*4887Schin                                 break;
497*4887Schin                         }
498*4887Schin                         incmd = 0;
499*4887Schin                         while(*cp++);
500*4887Schin                         if(cp>endbuff)
501*4887Schin                         {
502*4887Schin                                 incmd = 1;
503*4887Schin                                 break;
504*4887Schin                         }
505*4887Schin                         if(*cp==0 && ++cp>endbuff)
506*4887Schin                                 break;
507*4887Schin                 }
508*4887Schin                 size += n;
509*4887Schin 		sfread(iop,(char*)buff,n);
510*4887Schin 		if(incmd < 0)
511*4887Schin                 {
512*4887Schin 			if((n=sfread(iop,(char*)marker,4))==4)
513*4887Schin 			{
514*4887Schin 				n = (marker[0]<<16)|(marker[1]<<8)|marker[2];
515*4887Schin 				if(n < size/2)
516*4887Schin 				{
517*4887Schin 					hp->histmarker = hp->histcnt = size+4;
518*4887Schin 					return(n);
519*4887Schin 				}
520*4887Schin 				n=4;
521*4887Schin 			}
522*4887Schin 			if(n >0)
523*4887Schin 				size += n;
524*4887Schin 			incmd = 0;
525*4887Schin 		}
526*4887Schin 	}
527*4887Schin begin:
528*4887Schin 	sfseek(iop,(off_t)2,SEEK_SET);
529*4887Schin 	hp->histmarker = hp->histcnt = 2L;
530*4887Schin 	return(1);
531*4887Schin }
532*4887Schin 
533*4887Schin /*
534*4887Schin  * This routine reads the history file from the present position
535*4887Schin  * to the end-of-file and puts the information in the in-core
536*4887Schin  * history table
537*4887Schin  * Note that HIST_CMDNO is only recognized at the beginning of a command
538*4887Schin  * and that HIST_UNDO as the first character of a command is skipped
539*4887Schin  * unless it is followed by 0.  If followed by 0 then it cancels
540*4887Schin  * the previous command.
541*4887Schin  */
542*4887Schin 
543*4887Schin void hist_eof(register History_t *hp)
544*4887Schin {
545*4887Schin 	register char *cp,*first,*endbuff;
546*4887Schin 	register int incmd = 0;
547*4887Schin 	register off_t count = hp->histcnt;
548*4887Schin 	int n,skip=0;
549*4887Schin 	sfseek(hp->histfp,count,SEEK_SET);
550*4887Schin         while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0))
551*4887Schin 	{
552*4887Schin 		n = sfvalue(hp->histfp);
553*4887Schin 		*(endbuff = cp+n) = 0;
554*4887Schin 		first = cp += skip;
555*4887Schin 		while(1)
556*4887Schin 		{
557*4887Schin 			while(!incmd)
558*4887Schin 			{
559*4887Schin 				if(cp>first)
560*4887Schin 				{
561*4887Schin 					count += (cp-first);
562*4887Schin 					n = hist_ind(hp, ++hp->histind);
563*4887Schin #ifdef future
564*4887Schin 					if(count==hp->histcmds[n])
565*4887Schin 					{
566*4887Schin 	sfprintf(sfstderr,"count match n=%d\n",n);
567*4887Schin 						if(histinit)
568*4887Schin 						{
569*4887Schin 							histinit = 0;
570*4887Schin 							return;
571*4887Schin 						}
572*4887Schin 					}
573*4887Schin 					else if(n>=histinit)
574*4887Schin #endif
575*4887Schin 						hp->histcmds[n] = count;
576*4887Schin 					first = cp;
577*4887Schin 				}
578*4887Schin 				switch(*((unsigned char*)(cp++)))
579*4887Schin 				{
580*4887Schin 					case HIST_CMDNO:
581*4887Schin 						if(*cp==0)
582*4887Schin 						{
583*4887Schin 							hp->histmarker=count+2;
584*4887Schin 							cp += (HIST_MARKSZ-1);
585*4887Schin 							hp->histind--;
586*4887Schin #ifdef future
587*4887Schin 							if(cp <= endbuff)
588*4887Schin 							{
589*4887Schin 								unsigned char *marker = (unsigned char*)(cp-4);
590*4887Schin 								int n = ((marker[0]<<16)
591*4887Schin |(marker[1]<<8)|marker[2]);
592*4887Schin 								if((n<count/2) && n !=  (hp->histind+1))
593*4887Schin 									errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n);
594*4887Schin 							}
595*4887Schin #endif
596*4887Schin 						}
597*4887Schin 						break;
598*4887Schin 					case HIST_UNDO:
599*4887Schin 						if(*cp==0)
600*4887Schin 						{
601*4887Schin 							cp+=1;
602*4887Schin 							hp->histind-=2;
603*4887Schin 						}
604*4887Schin 						break;
605*4887Schin 					default:
606*4887Schin 						cp--;
607*4887Schin 						incmd = 1;
608*4887Schin 				}
609*4887Schin 				if(cp > endbuff)
610*4887Schin 				{
611*4887Schin 					cp++;
612*4887Schin 					goto refill;
613*4887Schin 				}
614*4887Schin 			}
615*4887Schin 			first = cp;
616*4887Schin 			while(*cp++);
617*4887Schin 			if(cp > endbuff)
618*4887Schin 				break;
619*4887Schin 			incmd = 0;
620*4887Schin 			while(*cp==0)
621*4887Schin 			{
622*4887Schin 				if(++cp > endbuff)
623*4887Schin 					goto refill;
624*4887Schin 			}
625*4887Schin 		}
626*4887Schin 	refill:
627*4887Schin 		count += (--cp-first);
628*4887Schin 		skip = (cp-endbuff);
629*4887Schin 		if(!incmd && !skip)
630*4887Schin 			hp->histcmds[hist_ind(hp,++hp->histind)] = count;
631*4887Schin 	}
632*4887Schin 	hp->histcnt = count;
633*4887Schin }
634*4887Schin 
635*4887Schin /*
636*4887Schin  * This routine will cause the previous command to be cancelled
637*4887Schin  */
638*4887Schin 
639*4887Schin void hist_cancel(register History_t *hp)
640*4887Schin {
641*4887Schin 	register int c;
642*4887Schin 	if(!hp)
643*4887Schin 		return;
644*4887Schin 	sfputc(hp->histfp,HIST_UNDO);
645*4887Schin 	sfputc(hp->histfp,0);
646*4887Schin 	sfsync(hp->histfp);
647*4887Schin 	hp->histcnt += 2;
648*4887Schin 	c = hist_ind(hp,--hp->histind);
649*4887Schin 	hp->histcmds[c] = hp->histcnt;
650*4887Schin }
651*4887Schin 
652*4887Schin /*
653*4887Schin  * flush the current history command
654*4887Schin  */
655*4887Schin 
656*4887Schin void hist_flush(register History_t *hp)
657*4887Schin {
658*4887Schin 	register char *buff;
659*4887Schin 	if(hp)
660*4887Schin 	{
661*4887Schin 		if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR))
662*4887Schin 		{
663*4887Schin 			hp->histflush = sfvalue(hp->histfp)+1;
664*4887Schin 			sfwrite(hp->histfp,buff,0);
665*4887Schin 		}
666*4887Schin 		else
667*4887Schin 			hp->histflush=0;
668*4887Schin 		if(sfsync(hp->histfp)<0)
669*4887Schin 		{
670*4887Schin 			hist_close(hp);
671*4887Schin 			if(!sh_histinit())
672*4887Schin 				sh_offoption(SH_HISTORY);
673*4887Schin 		}
674*4887Schin 		hp->histflush = 0;
675*4887Schin 	}
676*4887Schin }
677*4887Schin 
678*4887Schin /*
679*4887Schin  * This is the write discipline for the history file
680*4887Schin  * When called from hist_flush(), trailing newlines are deleted and
681*4887Schin  * a zero byte.  Line sequencing is added as required
682*4887Schin  */
683*4887Schin 
684*4887Schin #ifdef SF_BUFCONST
685*4887Schin static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle)
686*4887Schin #else
687*4887Schin static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle)
688*4887Schin #endif
689*4887Schin {
690*4887Schin 	register History_t *hp = (History_t*)handle;
691*4887Schin 	register char *bufptr = ((char*)buff)+insize;
692*4887Schin 	register int c,size = insize;
693*4887Schin 	register off_t cur;
694*4887Schin 	int saved=0;
695*4887Schin 	char saveptr[HIST_MARKSZ];
696*4887Schin 	if(!hp->histflush)
697*4887Schin 		return(write(sffileno(iop),(char*)buff,size));
698*4887Schin 	if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0)
699*4887Schin 	{
700*4887Schin 		errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno);
701*4887Schin 		return(-1);
702*4887Schin 	}
703*4887Schin 	hp->histcnt = cur;
704*4887Schin 	/* remove whitespace from end of commands */
705*4887Schin 	while(--bufptr >= (char*)buff)
706*4887Schin 	{
707*4887Schin 		c= *bufptr;
708*4887Schin 		if(!isspace(c))
709*4887Schin 		{
710*4887Schin 			if(c=='\\' && *(bufptr+1)!='\n')
711*4887Schin 				bufptr++;
712*4887Schin 			break;
713*4887Schin 		}
714*4887Schin 	}
715*4887Schin 	/* don't count empty lines */
716*4887Schin 	if(++bufptr <= (char*)buff)
717*4887Schin 		return(insize);
718*4887Schin 	*bufptr++ = '\n';
719*4887Schin 	*bufptr++ = 0;
720*4887Schin 	size = bufptr - (char*)buff;
721*4887Schin #if	SHOPT_ACCTFILE
722*4887Schin 	if(acctfd)
723*4887Schin 	{
724*4887Schin 		int timechars, offset;
725*4887Schin 		offset = staktell();
726*4887Schin 		stakputs(buff);
727*4887Schin 		stakseek(staktell() - 1);
728*4887Schin 		timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *)));
729*4887Schin 		lseek(acctfd, (off_t)0, SEEK_END);
730*4887Schin 		write(acctfd, stakptr(offset), size - 2 + timechars);
731*4887Schin 		stakseek(offset);
732*4887Schin 
733*4887Schin 	}
734*4887Schin #endif /* SHOPT_ACCTFILE */
735*4887Schin 	if(size&01)
736*4887Schin 	{
737*4887Schin 		size++;
738*4887Schin 		*bufptr++ = 0;
739*4887Schin 	}
740*4887Schin 	hp->histcnt +=  size;
741*4887Schin 	c = hist_ind(hp,++hp->histind);
742*4887Schin 	hp->histcmds[c] = hp->histcnt;
743*4887Schin 	if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2)
744*4887Schin 	{
745*4887Schin 		memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ);
746*4887Schin 		saved=1;
747*4887Schin 		hp->histcnt += HIST_MARKSZ;
748*4887Schin 		hist_marker(bufptr,hp->histind);
749*4887Schin 		hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt;
750*4887Schin 		size += HIST_MARKSZ;
751*4887Schin 	}
752*4887Schin 	errno = 0;
753*4887Schin 	size = write(sffileno(iop),(char*)buff,size);
754*4887Schin 	if(saved)
755*4887Schin 		memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ);
756*4887Schin 	if(size>=0)
757*4887Schin 	{
758*4887Schin 		hp->histwfail = 0;
759*4887Schin 		return(insize);
760*4887Schin 	}
761*4887Schin 	return(-1);
762*4887Schin }
763*4887Schin 
764*4887Schin /*
765*4887Schin  * Put history sequence number <n> into buffer <buff>
766*4887Schin  * The buffer must be large enough to hold HIST_MARKSZ chars
767*4887Schin  */
768*4887Schin 
769*4887Schin static void hist_marker(register char *buff,register long cmdno)
770*4887Schin {
771*4887Schin 	*buff++ = HIST_CMDNO;
772*4887Schin 	*buff++ = 0;
773*4887Schin 	*buff++ = (cmdno>>16);
774*4887Schin 	*buff++ = (cmdno>>8);
775*4887Schin 	*buff++ = cmdno;
776*4887Schin 	*buff++ = 0;
777*4887Schin }
778*4887Schin 
779*4887Schin /*
780*4887Schin  * return byte offset in history file for command <n>
781*4887Schin  */
782*4887Schin off_t hist_tell(register History_t *hp, int n)
783*4887Schin {
784*4887Schin 	return(hp->histcmds[hist_ind(hp,n)]);
785*4887Schin }
786*4887Schin 
787*4887Schin /*
788*4887Schin  * seek to the position of command <n>
789*4887Schin  */
790*4887Schin off_t hist_seek(register History_t *hp, int n)
791*4887Schin {
792*4887Schin 	return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET));
793*4887Schin }
794*4887Schin 
795*4887Schin /*
796*4887Schin  * write the command starting at offset <offset> onto file <outfile>.
797*4887Schin  * if character <last> appears before newline it is deleted
798*4887Schin  * each new-line character is replaced with string <nl>.
799*4887Schin  */
800*4887Schin 
801*4887Schin void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl)
802*4887Schin {
803*4887Schin 	register int oldc=0;
804*4887Schin 	register int c;
805*4887Schin 	if(offset<0 || !hp)
806*4887Schin 	{
807*4887Schin 		sfputr(outfile,sh_translate(e_unknown),'\n');
808*4887Schin 		return;
809*4887Schin 	}
810*4887Schin 	sfseek(hp->histfp,offset,SEEK_SET);
811*4887Schin 	while((c = sfgetc(hp->histfp)) != EOF)
812*4887Schin 	{
813*4887Schin 		if(c && oldc=='\n')
814*4887Schin 			sfputr(outfile,nl,-1);
815*4887Schin 		else if(last && (c==0 || (c=='\n' && oldc==last)))
816*4887Schin 			return;
817*4887Schin 		else if(oldc)
818*4887Schin 			sfputc(outfile,oldc);
819*4887Schin 		oldc = c;
820*4887Schin 		if(c==0)
821*4887Schin 			return;
822*4887Schin 	}
823*4887Schin 	return;
824*4887Schin }
825*4887Schin 
826*4887Schin /*
827*4887Schin  * find index for last line with given string
828*4887Schin  * If flag==0 then line must begin with string
829*4887Schin  * direction < 1 for backwards search
830*4887Schin */
831*4887Schin 
832*4887Schin Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction)
833*4887Schin {
834*4887Schin 	register int index2;
835*4887Schin 	off_t offset;
836*4887Schin 	int *coffset=0;
837*4887Schin 	Histloc_t location;
838*4887Schin 	location.hist_command = -1;
839*4887Schin 	location.hist_char = 0;
840*4887Schin 	location.hist_line = 0;
841*4887Schin 	if(!hp)
842*4887Schin 		return(location);
843*4887Schin 	/* leading ^ means beginning of line unless escaped */
844*4887Schin 	if(flag)
845*4887Schin 	{
846*4887Schin 		index2 = *string;
847*4887Schin 		if(index2=='\\')
848*4887Schin 			string++;
849*4887Schin 		else if(index2=='^')
850*4887Schin 		{
851*4887Schin 			flag=0;
852*4887Schin 			string++;
853*4887Schin 		}
854*4887Schin 	}
855*4887Schin 	if(flag)
856*4887Schin 		coffset = &location.hist_char;
857*4887Schin 	index2 = (int)hp->histind;
858*4887Schin 	if(direction<0)
859*4887Schin 	{
860*4887Schin 		index2 -= hp->histsize;
861*4887Schin 		if(index2<1)
862*4887Schin 			index2 = 1;
863*4887Schin 		if(index1 <= index2)
864*4887Schin 			return(location);
865*4887Schin 	}
866*4887Schin 	else if(index1 >= index2)
867*4887Schin 		return(location);
868*4887Schin 	while(index1!=index2)
869*4887Schin 	{
870*4887Schin 		direction>0?++index1:--index1;
871*4887Schin 		offset = hist_tell(hp,index1);
872*4887Schin 		if((location.hist_line=hist_match(hp,offset,string,coffset))>=0)
873*4887Schin 		{
874*4887Schin 			location.hist_command = index1;
875*4887Schin 			return(location);
876*4887Schin 		}
877*4887Schin #if KSHELL
878*4887Schin 		/* allow a search to be aborted */
879*4887Schin 		if(sh.trapnote&SH_SIGSET)
880*4887Schin 			break;
881*4887Schin #endif /* KSHELL */
882*4887Schin 	}
883*4887Schin 	return(location);
884*4887Schin }
885*4887Schin 
886*4887Schin /*
887*4887Schin  * search for <string> in history file starting at location <offset>
888*4887Schin  * If coffset==0 then line must begin with string
889*4887Schin  * returns the line number of the match if successful, otherwise -1
890*4887Schin  */
891*4887Schin 
892*4887Schin int hist_match(register History_t *hp,off_t offset,char *string,int *coffset)
893*4887Schin {
894*4887Schin 	register unsigned char *first, *cp;
895*4887Schin 	register int m,n,c=1,line=0;
896*4887Schin #if SHOPT_MULTIBYTE
897*4887Schin 	mbinit();
898*4887Schin #endif /* SHOPT_MULTIBYTE */
899*4887Schin 	sfseek(hp->histfp,offset,SEEK_SET);
900*4887Schin 	if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0)))
901*4887Schin 		return(-1);
902*4887Schin 	m = sfvalue(hp->histfp);
903*4887Schin 	n = strlen(string);
904*4887Schin 	while(m > n)
905*4887Schin 	{
906*4887Schin 		if(*cp==*string && memcmp(cp,string,n)==0)
907*4887Schin 		{
908*4887Schin 			if(coffset)
909*4887Schin 				*coffset = (cp-first);
910*4887Schin 			return(line);
911*4887Schin 		}
912*4887Schin 		if(!coffset)
913*4887Schin 			break;
914*4887Schin 		if(*cp=='\n')
915*4887Schin 			line++;
916*4887Schin #if SHOPT_MULTIBYTE
917*4887Schin 		if((c=mbsize(cp)) < 0)
918*4887Schin 			c = 1;
919*4887Schin #endif /* SHOPT_MULTIBYTE */
920*4887Schin 		cp += c;
921*4887Schin 		m -= c;
922*4887Schin 	}
923*4887Schin 	return(-1);
924*4887Schin }
925*4887Schin 
926*4887Schin 
927*4887Schin #if SHOPT_ESH || SHOPT_VSH
928*4887Schin /*
929*4887Schin  * copy command <command> from history file to s1
930*4887Schin  * at most <size> characters copied
931*4887Schin  * if s1==0 the number of lines for the command is returned
932*4887Schin  * line=linenumber  for emacs copy and only this line of command will be copied
933*4887Schin  * line < 0 for full command copy
934*4887Schin  * -1 returned if there is no history file
935*4887Schin  */
936*4887Schin 
937*4887Schin int hist_copy(char *s1,int size,int command,int line)
938*4887Schin {
939*4887Schin 	register int c;
940*4887Schin 	register History_t *hp = sh_getinterp()->hist_ptr;
941*4887Schin 	register int count = 0;
942*4887Schin 	register char *s1max = s1+size;
943*4887Schin 	if(!hp)
944*4887Schin 		return(-1);
945*4887Schin 	hist_seek(hp,command);
946*4887Schin 	while ((c = sfgetc(hp->histfp)) && c!=EOF)
947*4887Schin 	{
948*4887Schin 		if(c=='\n')
949*4887Schin 		{
950*4887Schin 			if(count++ ==line)
951*4887Schin 				break;
952*4887Schin 			else if(line >= 0)
953*4887Schin 				continue;
954*4887Schin 		}
955*4887Schin 		if(s1 && (line<0 || line==count))
956*4887Schin 		{
957*4887Schin 			if(s1 >= s1max)
958*4887Schin 			{
959*4887Schin 				*--s1 = 0;
960*4887Schin 				break;
961*4887Schin 			}
962*4887Schin 			*s1++ = c;
963*4887Schin 		}
964*4887Schin 
965*4887Schin 	}
966*4887Schin 	sfseek(hp->histfp,(off_t)0,SEEK_END);
967*4887Schin 	if(s1==0)
968*4887Schin 		return(count);
969*4887Schin 	if(count && (c= *(s1-1)) == '\n')
970*4887Schin 		s1--;
971*4887Schin 	*s1 = '\0';
972*4887Schin 	return(count);
973*4887Schin }
974*4887Schin 
975*4887Schin /*
976*4887Schin  * return word number <word> from command number <command>
977*4887Schin  */
978*4887Schin 
979*4887Schin char *hist_word(char *string,int size,int word)
980*4887Schin {
981*4887Schin 	register int c;
982*4887Schin 	register char *s1 = string;
983*4887Schin 	register unsigned char *cp = (unsigned char*)s1;
984*4887Schin 	register int flag = 0;
985*4887Schin 	History_t *hp = hist_ptr;
986*4887Schin 	if(!hp)
987*4887Schin #if KSHELL
988*4887Schin 	{
989*4887Schin 		strncpy(string,sh.lastarg,size);
990*4887Schin 		return(string);
991*4887Schin 	}
992*4887Schin #else
993*4887Schin 		return(NIL(char*));
994*4887Schin #endif /* KSHELL */
995*4887Schin 	hist_copy(string,size,(int)hp->histind-1,-1);
996*4887Schin 	for(;c = *cp;cp++)
997*4887Schin 	{
998*4887Schin 		c = isspace(c);
999*4887Schin 		if(c && flag)
1000*4887Schin 		{
1001*4887Schin 			*cp = 0;
1002*4887Schin 			if(--word==0)
1003*4887Schin 				break;
1004*4887Schin 			flag = 0;
1005*4887Schin 		}
1006*4887Schin 		else if(c==0 && flag==0)
1007*4887Schin 		{
1008*4887Schin 			s1 = (char*)cp;
1009*4887Schin 			flag++;
1010*4887Schin 		}
1011*4887Schin 	}
1012*4887Schin 	*cp = 0;
1013*4887Schin 	if(s1 != string)
1014*4887Schin 		strcpy(string,s1);
1015*4887Schin 	return(string);
1016*4887Schin }
1017*4887Schin 
1018*4887Schin #endif	/* SHOPT_ESH */
1019*4887Schin 
1020*4887Schin #if SHOPT_ESH
1021*4887Schin /*
1022*4887Schin  * given the current command and line number,
1023*4887Schin  * and number of lines back or foward,
1024*4887Schin  * compute the new command and line number.
1025*4887Schin  */
1026*4887Schin 
1027*4887Schin Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines)
1028*4887Schin {
1029*4887Schin 	Histloc_t next;
1030*4887Schin 	line += lines;
1031*4887Schin 	if(!hp)
1032*4887Schin 	{
1033*4887Schin 		command = -1;
1034*4887Schin 		goto done;
1035*4887Schin 	}
1036*4887Schin 	if(lines > 0)
1037*4887Schin 	{
1038*4887Schin 		register int count;
1039*4887Schin 		while(command <= hp->histind)
1040*4887Schin 		{
1041*4887Schin 			count = hist_copy(NIL(char*),0, command,-1);
1042*4887Schin 			if(count > line)
1043*4887Schin 				goto done;
1044*4887Schin 			line -= count;
1045*4887Schin 			command++;
1046*4887Schin 		}
1047*4887Schin 	}
1048*4887Schin 	else
1049*4887Schin 	{
1050*4887Schin 		register int least = (int)hp->histind-hp->histsize;
1051*4887Schin 		while(1)
1052*4887Schin 		{
1053*4887Schin 			if(line >=0)
1054*4887Schin 				goto done;
1055*4887Schin 			if(--command < least)
1056*4887Schin 				break;
1057*4887Schin 			line += hist_copy(NIL(char*),0, command,-1);
1058*4887Schin 		}
1059*4887Schin 		command = -1;
1060*4887Schin 	}
1061*4887Schin done:
1062*4887Schin 	next.hist_line = line;
1063*4887Schin 	next.hist_command = command;
1064*4887Schin 	return(next);
1065*4887Schin }
1066*4887Schin #endif	/* SHOPT_ESH */
1067*4887Schin 
1068*4887Schin 
1069*4887Schin /*
1070*4887Schin  * Handle history file exceptions
1071*4887Schin  */
1072*4887Schin #ifdef SF_BUFCONST
1073*4887Schin static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle)
1074*4887Schin #else
1075*4887Schin static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle)
1076*4887Schin #endif
1077*4887Schin {
1078*4887Schin 	register int newfd,oldfd;
1079*4887Schin 	History_t *hp = (History_t*)handle;
1080*4887Schin 	if(type==SF_WRITE)
1081*4887Schin 	{
1082*4887Schin 		if(errno==ENOSPC || hp->histwfail++ >= 10)
1083*4887Schin 			return(0);
1084*4887Schin 		/* write failure could be NFS problem, try to re-open */
1085*4887Schin 		close(oldfd=sffileno(fp));
1086*4887Schin 		if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0)
1087*4887Schin 		{
1088*4887Schin 			if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd)
1089*4887Schin 				return(-1);
1090*4887Schin 			fcntl(oldfd,F_SETFD,FD_CLOEXEC);
1091*4887Schin 			close(newfd);
1092*4887Schin 			if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt)
1093*4887Schin 			{
1094*4887Schin 				register int index = hp->histind;
1095*4887Schin 				lseek(oldfd,(off_t)2,SEEK_SET);
1096*4887Schin 				hp->histcnt = 2;
1097*4887Schin 				hp->histind = 1;
1098*4887Schin 				hp->histcmds[1] = 2;
1099*4887Schin 				hist_eof(hp);
1100*4887Schin 				hp->histmarker = hp->histcnt;
1101*4887Schin 				hp->histind = index;
1102*4887Schin 			}
1103*4887Schin 			return(1);
1104*4887Schin 		}
1105*4887Schin 		errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname);
1106*4887Schin 		return(-1);
1107*4887Schin 	}
1108*4887Schin 	return(0);
1109*4887Schin }
1110