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