1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1982-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
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)
92da2e3ebdSchin # define path_relative(x) (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 }
165da2e3ebdSchin if(strmatch(acctfile,e_devfdNN))
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 {
1807c2fbfb3SApril Chin Shell_t *shp = (Shell_t*)hp->histshell;
1817c2fbfb3SApril Chin char *buff, *cp, *last;
1827c2fbfb3SApril Chin int id1, id2, r=0, n, fd;
1837c2fbfb3SApril Chin if((fd=open(name, O_RDONLY)) < 0)
1847c2fbfb3SApril Chin return(0);
1857c2fbfb3SApril Chin if((n = read(fd, logbuf,len-1)) < 0)
1867c2fbfb3SApril Chin goto done;
1877c2fbfb3SApril Chin while(logbuf[n-1]=='\n')
1887c2fbfb3SApril Chin n--;
1897c2fbfb3SApril Chin logbuf[n] = 0;
1907c2fbfb3SApril Chin if(!(cp=strchr(logbuf,';')) && !(cp=strchr(logbuf,' ')))
1917c2fbfb3SApril Chin goto done;
1927c2fbfb3SApril Chin *cp = 0;
1937c2fbfb3SApril Chin do
1947c2fbfb3SApril Chin {
1957c2fbfb3SApril Chin cp++;
1967c2fbfb3SApril Chin id1 = id2 = strtol(cp,&last,10);
1977c2fbfb3SApril Chin if(*last=='-')
1987c2fbfb3SApril Chin id1 = strtol(last+1,&last,10);
1997c2fbfb3SApril Chin if(shp->euserid >=id1 && shp->euserid <= id2)
2007c2fbfb3SApril Chin r |= 1;
2017c2fbfb3SApril Chin if(shp->userid >=id1 && shp->userid <= id2)
2027c2fbfb3SApril Chin r |= 2;
2037c2fbfb3SApril Chin cp = last;
2047c2fbfb3SApril Chin }
2057c2fbfb3SApril Chin while(*cp==';' || *cp==' ');
2067c2fbfb3SApril Chin done:
2077c2fbfb3SApril Chin close(fd);
2087c2fbfb3SApril Chin return(r);
2097c2fbfb3SApril Chin
2107c2fbfb3SApril Chin }
2117c2fbfb3SApril Chin #endif /*SHOPT_AUDIT*/
2127c2fbfb3SApril Chin
213da2e3ebdSchin static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION };
214da2e3ebdSchin static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL};
215da2e3ebdSchin
hist_touch(void * handle)216da2e3ebdSchin static void hist_touch(void *handle)
217da2e3ebdSchin {
218da2e3ebdSchin touch((char*)handle, (time_t)0, (time_t)0, 0);
219da2e3ebdSchin }
220da2e3ebdSchin
221da2e3ebdSchin /*
222da2e3ebdSchin * open the history file
223da2e3ebdSchin * if HISTNAME is not given and userid==0 then no history file.
224da2e3ebdSchin * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is
225da2e3ebdSchin * cleaned up.
226da2e3ebdSchin * hist_open() returns 1, if history file is open
227da2e3ebdSchin */
sh_histinit(void * sh_context)2287c2fbfb3SApril Chin int sh_histinit(void *sh_context)
229da2e3ebdSchin {
2307c2fbfb3SApril Chin Shell_t *shp = (Shell_t*)sh_context;
231da2e3ebdSchin register int fd;
232da2e3ebdSchin register History_t *hp;
233da2e3ebdSchin register char *histname;
234da2e3ebdSchin char *fname=0;
235da2e3ebdSchin int histmask, maxlines, hist_start=0;
236da2e3ebdSchin register char *cp;
237da2e3ebdSchin register off_t hsize = 0;
238da2e3ebdSchin
2397c2fbfb3SApril Chin if(shp->hist_ptr=hist_ptr)
240da2e3ebdSchin return(1);
241da2e3ebdSchin if(!(histname = nv_getval(HISTFILE)))
242da2e3ebdSchin {
243da2e3ebdSchin int offset = staktell();
244da2e3ebdSchin if(cp=nv_getval(HOME))
245da2e3ebdSchin stakputs(cp);
246da2e3ebdSchin stakputs(hist_fname);
247da2e3ebdSchin stakputc(0);
248da2e3ebdSchin stakseek(offset);
249da2e3ebdSchin histname = stakptr(offset);
250da2e3ebdSchin }
251da2e3ebdSchin #ifdef future
252da2e3ebdSchin if(hp=wasopen)
253da2e3ebdSchin {
254da2e3ebdSchin /* reuse history file if same name */
255da2e3ebdSchin wasopen = 0;
2567c2fbfb3SApril Chin shp->hist_ptr = hist_ptr = hp;
257da2e3ebdSchin if(strcmp(histname,hp->histname)==0)
258da2e3ebdSchin return(1);
259da2e3ebdSchin else
260da2e3ebdSchin hist_free();
261da2e3ebdSchin }
262da2e3ebdSchin #endif
263da2e3ebdSchin retry:
264da2e3ebdSchin cp = path_relative(histname);
265da2e3ebdSchin if(!histinit)
266da2e3ebdSchin histmode = S_IRUSR|S_IWUSR;
267da2e3ebdSchin if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0)
268da2e3ebdSchin {
269da2e3ebdSchin hsize=lseek(fd,(off_t)0,SEEK_END);
270da2e3ebdSchin }
271da2e3ebdSchin if((unsigned)fd <=2)
272da2e3ebdSchin {
273da2e3ebdSchin int n;
274da2e3ebdSchin if((n=fcntl(fd,F_DUPFD,10))>=0)
275da2e3ebdSchin {
276da2e3ebdSchin close(fd);
277da2e3ebdSchin fd=n;
278da2e3ebdSchin }
279da2e3ebdSchin }
280da2e3ebdSchin /* make sure that file has history file format */
281da2e3ebdSchin if(hsize && hist_check(fd))
282da2e3ebdSchin {
283da2e3ebdSchin close(fd);
284da2e3ebdSchin hsize = 0;
285da2e3ebdSchin if(unlink(cp)>=0)
286da2e3ebdSchin goto retry;
287da2e3ebdSchin fd = -1;
288da2e3ebdSchin }
289da2e3ebdSchin if(fd < 0)
290da2e3ebdSchin {
291da2e3ebdSchin #if KSHELL
292da2e3ebdSchin /* don't allow root a history_file in /tmp */
2937c2fbfb3SApril Chin if(shp->userid)
294da2e3ebdSchin #endif /* KSHELL */
295da2e3ebdSchin {
296da2e3ebdSchin if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*))))
297da2e3ebdSchin return(0);
298da2e3ebdSchin fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
299da2e3ebdSchin }
300da2e3ebdSchin }
301da2e3ebdSchin if(fd<0)
302da2e3ebdSchin return(0);
303da2e3ebdSchin /* set the file to close-on-exec */
304da2e3ebdSchin fcntl(fd,F_SETFD,FD_CLOEXEC);
305da2e3ebdSchin if(cp=nv_getval(HISTSIZE))
306da2e3ebdSchin maxlines = (unsigned)strtol(cp, (char**)0, 10);
307da2e3ebdSchin else
308da2e3ebdSchin maxlines = HIST_DFLT;
309da2e3ebdSchin for(histmask=16;histmask <= maxlines; histmask <<=1 );
310da2e3ebdSchin if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t))))
311da2e3ebdSchin {
312da2e3ebdSchin close(fd);
313da2e3ebdSchin return(0);
314da2e3ebdSchin }
3157c2fbfb3SApril Chin shp->hist_ptr = hist_ptr = hp;
3167c2fbfb3SApril Chin hp->histshell = (void*)shp;
317da2e3ebdSchin hp->histsize = maxlines;
318da2e3ebdSchin hp->histmask = histmask;
319da2e3ebdSchin hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE);
320da2e3ebdSchin memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1));
321da2e3ebdSchin hp->histind = 1;
322da2e3ebdSchin hp->histcmds[1] = 2;
323da2e3ebdSchin hp->histcnt = 2;
324da2e3ebdSchin hp->histname = strdup(histname);
325da2e3ebdSchin hp->histdisc = hist_disc;
326da2e3ebdSchin if(hsize==0)
327da2e3ebdSchin {
328da2e3ebdSchin /* put special characters at front of file */
329da2e3ebdSchin sfwrite(hp->histfp,(char*)hist_stamp,2);
330da2e3ebdSchin sfsync(hp->histfp);
331da2e3ebdSchin }
332da2e3ebdSchin /* initialize history list */
333da2e3ebdSchin else
334da2e3ebdSchin {
335da2e3ebdSchin int first,last;
336da2e3ebdSchin off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE;
337da2e3ebdSchin hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size);
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 {
3977c2fbfb3SApril Chin hp->tty = strdup(ttyname(2));
3987c2fbfb3SApril Chin hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE);
3997c2fbfb3SApril Chin }
4007c2fbfb3SApril Chin }
4017c2fbfb3SApril Chin }
4027c2fbfb3SApril Chin #endif
403da2e3ebdSchin return(1);
404da2e3ebdSchin }
405da2e3ebdSchin
406da2e3ebdSchin /*
407da2e3ebdSchin * close the history file and free the space
408da2e3ebdSchin */
409da2e3ebdSchin
hist_close(register History_t * hp)410da2e3ebdSchin void hist_close(register History_t *hp)
411da2e3ebdSchin {
4127c2fbfb3SApril Chin Shell_t *shp = (Shell_t*)hp->histshell;
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;
4247c2fbfb3SApril Chin shp->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)
484da2e3ebdSchin tmpname = name;
485da2e3ebdSchin fd = open(tmpname,O_RDONLY);
486da2e3ebdSchin sfsetfd(hist_old->histfp,fd);
487da2e3ebdSchin if(tmpname==name)
488da2e3ebdSchin tmpname = 0;
489da2e3ebdSchin }
4907c2fbfb3SApril Chin hist_ptr = 0;
491da2e3ebdSchin if(fstat(sffileno(hist_old->histfp),&statb)>=0)
492da2e3ebdSchin {
493da2e3ebdSchin histinit = 1;
494da2e3ebdSchin histmode = statb.st_mode;
495da2e3ebdSchin }
4967c2fbfb3SApril Chin if(!sh_histinit(hp->histshell))
497da2e3ebdSchin {
498da2e3ebdSchin /* use the old history file */
4997c2fbfb3SApril Chin return hist_ptr = hist_old;
500da2e3ebdSchin }
501da2e3ebdSchin hist_new = hist_ptr;
502da2e3ebdSchin hist_ptr = hist_old;
503da2e3ebdSchin if(--n < 0)
504da2e3ebdSchin n = 0;
505da2e3ebdSchin newp = hist_seek(hist_old,++n);
506da2e3ebdSchin while(1)
507da2e3ebdSchin {
508da2e3ebdSchin if(!incmd)
509da2e3ebdSchin {
510da2e3ebdSchin c = hist_ind(hist_new,++hist_new->histind);
511da2e3ebdSchin hist_new->histcmds[c] = hist_new->histcnt;
512da2e3ebdSchin if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2)
513da2e3ebdSchin {
514da2e3ebdSchin char locbuff[HIST_MARKSZ];
515da2e3ebdSchin hist_marker(locbuff,hist_new->histind);
516da2e3ebdSchin sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ);
517da2e3ebdSchin hist_new->histcnt += HIST_MARKSZ;
518da2e3ebdSchin hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt;
519da2e3ebdSchin }
520da2e3ebdSchin oldp = newp;
521da2e3ebdSchin newp = hist_seek(hist_old,++n);
522da2e3ebdSchin if(newp <=oldp)
523da2e3ebdSchin break;
524da2e3ebdSchin }
525da2e3ebdSchin if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0)))
526da2e3ebdSchin break;
527da2e3ebdSchin *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0;
528da2e3ebdSchin /* copy to null byte */
529da2e3ebdSchin incmd = 0;
530da2e3ebdSchin while(*cp++);
531da2e3ebdSchin if(cp > endbuff)
532da2e3ebdSchin incmd = 1;
533da2e3ebdSchin else if(*cp==0)
534da2e3ebdSchin cp++;
535da2e3ebdSchin if(cp > endbuff)
536da2e3ebdSchin cp = endbuff;
537da2e3ebdSchin c = cp-buff;
538da2e3ebdSchin hist_new->histcnt += c;
539da2e3ebdSchin sfwrite(hist_new->histfp,buff,c);
540da2e3ebdSchin }
5417c2fbfb3SApril Chin hist_cancel(hist_new);
542da2e3ebdSchin sfclose(hist_old->histfp);
543da2e3ebdSchin if(tmpname)
544da2e3ebdSchin {
545da2e3ebdSchin unlink(tmpname);
546da2e3ebdSchin free(tmpname);
547da2e3ebdSchin }
548da2e3ebdSchin free((char*)hist_old);
5497c2fbfb3SApril Chin return hist_ptr = hist_new;
550da2e3ebdSchin }
551da2e3ebdSchin
552da2e3ebdSchin /*
553da2e3ebdSchin * position history file at size and find next command number
554da2e3ebdSchin */
hist_nearend(History_t * hp,Sfio_t * iop,register off_t size)555da2e3ebdSchin static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size)
556da2e3ebdSchin {
557da2e3ebdSchin register unsigned char *cp, *endbuff;
558da2e3ebdSchin register int n, incmd=1;
559da2e3ebdSchin unsigned char *buff, marker[4];
560da2e3ebdSchin if(size <= 2L || sfseek(iop,size,SEEK_SET)<0)
561da2e3ebdSchin goto begin;
562da2e3ebdSchin /* skip to marker command and return the number */
563da2e3ebdSchin /* numbering commands occur after a null and begin with HIST_CMDNO */
564da2e3ebdSchin while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR))
565da2e3ebdSchin {
566da2e3ebdSchin n = sfvalue(iop);
567da2e3ebdSchin *(endbuff=cp+n) = 0;
568da2e3ebdSchin while(1)
569da2e3ebdSchin {
570da2e3ebdSchin /* check for marker */
571da2e3ebdSchin if(!incmd && *cp++==HIST_CMDNO && *cp==0)
572da2e3ebdSchin {
573da2e3ebdSchin n = cp+1 - buff;
574da2e3ebdSchin incmd = -1;
575da2e3ebdSchin break;
576da2e3ebdSchin }
577da2e3ebdSchin incmd = 0;
578da2e3ebdSchin while(*cp++);
579da2e3ebdSchin if(cp>endbuff)
580da2e3ebdSchin {
581da2e3ebdSchin incmd = 1;
582da2e3ebdSchin break;
583da2e3ebdSchin }
584da2e3ebdSchin if(*cp==0 && ++cp>endbuff)
585da2e3ebdSchin break;
586da2e3ebdSchin }
587da2e3ebdSchin size += n;
588da2e3ebdSchin sfread(iop,(char*)buff,n);
589da2e3ebdSchin if(incmd < 0)
590da2e3ebdSchin {
591da2e3ebdSchin if((n=sfread(iop,(char*)marker,4))==4)
592da2e3ebdSchin {
593da2e3ebdSchin n = (marker[0]<<16)|(marker[1]<<8)|marker[2];
594da2e3ebdSchin if(n < size/2)
595da2e3ebdSchin {
596da2e3ebdSchin hp->histmarker = hp->histcnt = size+4;
597da2e3ebdSchin return(n);
598da2e3ebdSchin }
599da2e3ebdSchin n=4;
600da2e3ebdSchin }
601da2e3ebdSchin if(n >0)
602da2e3ebdSchin size += n;
603da2e3ebdSchin incmd = 0;
604da2e3ebdSchin }
605da2e3ebdSchin }
606da2e3ebdSchin begin:
607da2e3ebdSchin sfseek(iop,(off_t)2,SEEK_SET);
608da2e3ebdSchin hp->histmarker = hp->histcnt = 2L;
609da2e3ebdSchin return(1);
610da2e3ebdSchin }
611da2e3ebdSchin
612da2e3ebdSchin /*
613da2e3ebdSchin * This routine reads the history file from the present position
614da2e3ebdSchin * to the end-of-file and puts the information in the in-core
615da2e3ebdSchin * history table
616da2e3ebdSchin * Note that HIST_CMDNO is only recognized at the beginning of a command
617da2e3ebdSchin * and that HIST_UNDO as the first character of a command is skipped
618da2e3ebdSchin * unless it is followed by 0. If followed by 0 then it cancels
619da2e3ebdSchin * the previous command.
620da2e3ebdSchin */
621da2e3ebdSchin
hist_eof(register History_t * hp)622da2e3ebdSchin void hist_eof(register History_t *hp)
623da2e3ebdSchin {
624da2e3ebdSchin register char *cp,*first,*endbuff;
625da2e3ebdSchin register int incmd = 0;
626da2e3ebdSchin register off_t count = hp->histcnt;
627da2e3ebdSchin int n,skip=0;
628da2e3ebdSchin sfseek(hp->histfp,count,SEEK_SET);
629da2e3ebdSchin while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0))
630da2e3ebdSchin {
631da2e3ebdSchin n = sfvalue(hp->histfp);
632da2e3ebdSchin *(endbuff = cp+n) = 0;
633da2e3ebdSchin first = cp += skip;
634da2e3ebdSchin while(1)
635da2e3ebdSchin {
636da2e3ebdSchin while(!incmd)
637da2e3ebdSchin {
638da2e3ebdSchin if(cp>first)
639da2e3ebdSchin {
640da2e3ebdSchin count += (cp-first);
641da2e3ebdSchin n = hist_ind(hp, ++hp->histind);
642da2e3ebdSchin #ifdef future
643da2e3ebdSchin if(count==hp->histcmds[n])
644da2e3ebdSchin {
645da2e3ebdSchin sfprintf(sfstderr,"count match n=%d\n",n);
646da2e3ebdSchin if(histinit)
647da2e3ebdSchin {
648da2e3ebdSchin histinit = 0;
649da2e3ebdSchin return;
650da2e3ebdSchin }
651da2e3ebdSchin }
652da2e3ebdSchin else if(n>=histinit)
653da2e3ebdSchin #endif
654da2e3ebdSchin hp->histcmds[n] = count;
655da2e3ebdSchin first = cp;
656da2e3ebdSchin }
657da2e3ebdSchin switch(*((unsigned char*)(cp++)))
658da2e3ebdSchin {
659da2e3ebdSchin case HIST_CMDNO:
660da2e3ebdSchin if(*cp==0)
661da2e3ebdSchin {
662da2e3ebdSchin hp->histmarker=count+2;
663da2e3ebdSchin cp += (HIST_MARKSZ-1);
664da2e3ebdSchin hp->histind--;
665da2e3ebdSchin #ifdef future
666da2e3ebdSchin if(cp <= endbuff)
667da2e3ebdSchin {
668da2e3ebdSchin unsigned char *marker = (unsigned char*)(cp-4);
669da2e3ebdSchin int n = ((marker[0]<<16)
670da2e3ebdSchin |(marker[1]<<8)|marker[2]);
671da2e3ebdSchin if((n<count/2) && n != (hp->histind+1))
672da2e3ebdSchin errormsg(SH_DICT,2,"index=%d marker=%d", hp->histind, n);
673da2e3ebdSchin }
674da2e3ebdSchin #endif
675da2e3ebdSchin }
676da2e3ebdSchin break;
677da2e3ebdSchin case HIST_UNDO:
678da2e3ebdSchin if(*cp==0)
679da2e3ebdSchin {
680da2e3ebdSchin cp+=1;
681da2e3ebdSchin hp->histind-=2;
682da2e3ebdSchin }
683da2e3ebdSchin break;
684da2e3ebdSchin default:
685da2e3ebdSchin cp--;
686da2e3ebdSchin incmd = 1;
687da2e3ebdSchin }
688da2e3ebdSchin if(cp > endbuff)
689da2e3ebdSchin {
690da2e3ebdSchin cp++;
691da2e3ebdSchin goto refill;
692da2e3ebdSchin }
693da2e3ebdSchin }
694da2e3ebdSchin first = cp;
695da2e3ebdSchin while(*cp++);
696da2e3ebdSchin if(cp > endbuff)
697da2e3ebdSchin break;
698da2e3ebdSchin incmd = 0;
699da2e3ebdSchin while(*cp==0)
700da2e3ebdSchin {
701da2e3ebdSchin if(++cp > endbuff)
702da2e3ebdSchin goto refill;
703da2e3ebdSchin }
704da2e3ebdSchin }
705da2e3ebdSchin refill:
706da2e3ebdSchin count += (--cp-first);
707da2e3ebdSchin skip = (cp-endbuff);
708da2e3ebdSchin if(!incmd && !skip)
709da2e3ebdSchin hp->histcmds[hist_ind(hp,++hp->histind)] = count;
710da2e3ebdSchin }
711da2e3ebdSchin hp->histcnt = count;
712da2e3ebdSchin }
713da2e3ebdSchin
714da2e3ebdSchin /*
715da2e3ebdSchin * This routine will cause the previous command to be cancelled
716da2e3ebdSchin */
717da2e3ebdSchin
hist_cancel(register History_t * hp)718da2e3ebdSchin void hist_cancel(register History_t *hp)
719da2e3ebdSchin {
720da2e3ebdSchin register int c;
721da2e3ebdSchin if(!hp)
722da2e3ebdSchin return;
723da2e3ebdSchin sfputc(hp->histfp,HIST_UNDO);
724da2e3ebdSchin sfputc(hp->histfp,0);
725da2e3ebdSchin sfsync(hp->histfp);
726da2e3ebdSchin hp->histcnt += 2;
727da2e3ebdSchin c = hist_ind(hp,--hp->histind);
728da2e3ebdSchin hp->histcmds[c] = hp->histcnt;
729da2e3ebdSchin }
730da2e3ebdSchin
731da2e3ebdSchin /*
732da2e3ebdSchin * flush the current history command
733da2e3ebdSchin */
734da2e3ebdSchin
hist_flush(register History_t * hp)735da2e3ebdSchin void hist_flush(register History_t *hp)
736da2e3ebdSchin {
737da2e3ebdSchin register char *buff;
738da2e3ebdSchin if(hp)
739da2e3ebdSchin {
740da2e3ebdSchin if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR))
741da2e3ebdSchin {
742da2e3ebdSchin hp->histflush = sfvalue(hp->histfp)+1;
743da2e3ebdSchin sfwrite(hp->histfp,buff,0);
744da2e3ebdSchin }
745da2e3ebdSchin else
746da2e3ebdSchin hp->histflush=0;
747da2e3ebdSchin if(sfsync(hp->histfp)<0)
748da2e3ebdSchin {
749da2e3ebdSchin hist_close(hp);
7507c2fbfb3SApril Chin if(!sh_histinit(hp->histshell))
751da2e3ebdSchin sh_offoption(SH_HISTORY);
752da2e3ebdSchin }
753da2e3ebdSchin hp->histflush = 0;
754da2e3ebdSchin }
755da2e3ebdSchin }
756da2e3ebdSchin
757da2e3ebdSchin /*
758da2e3ebdSchin * This is the write discipline for the history file
759da2e3ebdSchin * When called from hist_flush(), trailing newlines are deleted and
760da2e3ebdSchin * a zero byte. Line sequencing is added as required
761da2e3ebdSchin */
762da2e3ebdSchin
763da2e3ebdSchin #ifdef SF_BUFCONST
hist_write(Sfio_t * iop,const void * buff,register size_t insize,Sfdisc_t * handle)764da2e3ebdSchin static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle)
765da2e3ebdSchin #else
766da2e3ebdSchin static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle)
767da2e3ebdSchin #endif
768da2e3ebdSchin {
769da2e3ebdSchin register History_t *hp = (History_t*)handle;
770da2e3ebdSchin register char *bufptr = ((char*)buff)+insize;
771da2e3ebdSchin register int c,size = insize;
772da2e3ebdSchin register off_t cur;
773da2e3ebdSchin int saved=0;
774da2e3ebdSchin char saveptr[HIST_MARKSZ];
775da2e3ebdSchin if(!hp->histflush)
776da2e3ebdSchin return(write(sffileno(iop),(char*)buff,size));
777da2e3ebdSchin if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0)
778da2e3ebdSchin {
779da2e3ebdSchin errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno);
780da2e3ebdSchin return(-1);
781da2e3ebdSchin }
782da2e3ebdSchin hp->histcnt = cur;
783da2e3ebdSchin /* remove whitespace from end of commands */
784da2e3ebdSchin while(--bufptr >= (char*)buff)
785da2e3ebdSchin {
786da2e3ebdSchin c= *bufptr;
787da2e3ebdSchin if(!isspace(c))
788da2e3ebdSchin {
789da2e3ebdSchin if(c=='\\' && *(bufptr+1)!='\n')
790da2e3ebdSchin bufptr++;
791da2e3ebdSchin break;
792da2e3ebdSchin }
793da2e3ebdSchin }
794da2e3ebdSchin /* don't count empty lines */
795da2e3ebdSchin if(++bufptr <= (char*)buff)
796da2e3ebdSchin return(insize);
797da2e3ebdSchin *bufptr++ = '\n';
798da2e3ebdSchin *bufptr++ = 0;
799da2e3ebdSchin size = bufptr - (char*)buff;
8007c2fbfb3SApril Chin #if SHOPT_AUDIT
8017c2fbfb3SApril Chin if(hp->auditfp)
8027c2fbfb3SApril Chin {
8037c2fbfb3SApril Chin Shell_t *shp = (Shell_t*)hp->histshell;
8047c2fbfb3SApril Chin time_t t=time((time_t*)0);
8057c2fbfb3SApril Chin sfprintf(hp->auditfp,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED)?shp->euserid:shp->userid,t,hp->tty,size,buff,0);
8067c2fbfb3SApril Chin sfsync(hp->auditfp);
8077c2fbfb3SApril Chin }
8087c2fbfb3SApril Chin #endif /* SHOPT_AUDIT */
809da2e3ebdSchin #if SHOPT_ACCTFILE
810da2e3ebdSchin if(acctfd)
811da2e3ebdSchin {
812da2e3ebdSchin int timechars, offset;
813da2e3ebdSchin offset = staktell();
814da2e3ebdSchin stakputs(buff);
815da2e3ebdSchin stakseek(staktell() - 1);
816da2e3ebdSchin timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *)));
817da2e3ebdSchin lseek(acctfd, (off_t)0, SEEK_END);
818da2e3ebdSchin write(acctfd, stakptr(offset), size - 2 + timechars);
819da2e3ebdSchin stakseek(offset);
820da2e3ebdSchin
821da2e3ebdSchin }
822da2e3ebdSchin #endif /* SHOPT_ACCTFILE */
823da2e3ebdSchin if(size&01)
824da2e3ebdSchin {
825da2e3ebdSchin size++;
826da2e3ebdSchin *bufptr++ = 0;
827da2e3ebdSchin }
828da2e3ebdSchin hp->histcnt += size;
829da2e3ebdSchin c = hist_ind(hp,++hp->histind);
830da2e3ebdSchin hp->histcmds[c] = hp->histcnt;
831da2e3ebdSchin if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2)
832da2e3ebdSchin {
833da2e3ebdSchin memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ);
834da2e3ebdSchin saved=1;
835da2e3ebdSchin hp->histcnt += HIST_MARKSZ;
836da2e3ebdSchin hist_marker(bufptr,hp->histind);
837da2e3ebdSchin hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt;
838da2e3ebdSchin size += HIST_MARKSZ;
839da2e3ebdSchin }
840da2e3ebdSchin errno = 0;
841da2e3ebdSchin size = write(sffileno(iop),(char*)buff,size);
842da2e3ebdSchin if(saved)
843da2e3ebdSchin memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ);
844da2e3ebdSchin if(size>=0)
845da2e3ebdSchin {
846da2e3ebdSchin hp->histwfail = 0;
847da2e3ebdSchin return(insize);
848da2e3ebdSchin }
849da2e3ebdSchin return(-1);
850da2e3ebdSchin }
851da2e3ebdSchin
852da2e3ebdSchin /*
853da2e3ebdSchin * Put history sequence number <n> into buffer <buff>
854da2e3ebdSchin * The buffer must be large enough to hold HIST_MARKSZ chars
855da2e3ebdSchin */
856da2e3ebdSchin
hist_marker(register char * buff,register long cmdno)857da2e3ebdSchin static void hist_marker(register char *buff,register long cmdno)
858da2e3ebdSchin {
859da2e3ebdSchin *buff++ = HIST_CMDNO;
860da2e3ebdSchin *buff++ = 0;
861da2e3ebdSchin *buff++ = (cmdno>>16);
862da2e3ebdSchin *buff++ = (cmdno>>8);
863da2e3ebdSchin *buff++ = cmdno;
864da2e3ebdSchin *buff++ = 0;
865da2e3ebdSchin }
866da2e3ebdSchin
867da2e3ebdSchin /*
868da2e3ebdSchin * return byte offset in history file for command <n>
869da2e3ebdSchin */
hist_tell(register History_t * hp,int n)870da2e3ebdSchin off_t hist_tell(register History_t *hp, int n)
871da2e3ebdSchin {
872da2e3ebdSchin return(hp->histcmds[hist_ind(hp,n)]);
873da2e3ebdSchin }
874da2e3ebdSchin
875da2e3ebdSchin /*
876da2e3ebdSchin * seek to the position of command <n>
877da2e3ebdSchin */
hist_seek(register History_t * hp,int n)878da2e3ebdSchin off_t hist_seek(register History_t *hp, int n)
879da2e3ebdSchin {
880da2e3ebdSchin return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET));
881da2e3ebdSchin }
882da2e3ebdSchin
883da2e3ebdSchin /*
884da2e3ebdSchin * write the command starting at offset <offset> onto file <outfile>.
885da2e3ebdSchin * if character <last> appears before newline it is deleted
886da2e3ebdSchin * each new-line character is replaced with string <nl>.
887da2e3ebdSchin */
888da2e3ebdSchin
hist_list(register History_t * hp,Sfio_t * outfile,off_t offset,int last,char * nl)889da2e3ebdSchin void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl)
890da2e3ebdSchin {
891da2e3ebdSchin register int oldc=0;
892da2e3ebdSchin register int c;
893da2e3ebdSchin if(offset<0 || !hp)
894da2e3ebdSchin {
895da2e3ebdSchin sfputr(outfile,sh_translate(e_unknown),'\n');
896da2e3ebdSchin return;
897da2e3ebdSchin }
898da2e3ebdSchin sfseek(hp->histfp,offset,SEEK_SET);
899da2e3ebdSchin while((c = sfgetc(hp->histfp)) != EOF)
900da2e3ebdSchin {
901da2e3ebdSchin if(c && oldc=='\n')
902da2e3ebdSchin sfputr(outfile,nl,-1);
903da2e3ebdSchin else if(last && (c==0 || (c=='\n' && oldc==last)))
904da2e3ebdSchin return;
905da2e3ebdSchin else if(oldc)
906da2e3ebdSchin sfputc(outfile,oldc);
907da2e3ebdSchin oldc = c;
908da2e3ebdSchin if(c==0)
909da2e3ebdSchin return;
910da2e3ebdSchin }
911da2e3ebdSchin return;
912da2e3ebdSchin }
913da2e3ebdSchin
914da2e3ebdSchin /*
915da2e3ebdSchin * find index for last line with given string
916da2e3ebdSchin * If flag==0 then line must begin with string
917da2e3ebdSchin * direction < 1 for backwards search
918da2e3ebdSchin */
919da2e3ebdSchin
hist_find(register History_t * hp,char * string,register int index1,int flag,int direction)920da2e3ebdSchin Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction)
921da2e3ebdSchin {
922da2e3ebdSchin register int index2;
923da2e3ebdSchin off_t offset;
924da2e3ebdSchin int *coffset=0;
925da2e3ebdSchin Histloc_t location;
926da2e3ebdSchin location.hist_command = -1;
927da2e3ebdSchin location.hist_char = 0;
928da2e3ebdSchin location.hist_line = 0;
929da2e3ebdSchin if(!hp)
930da2e3ebdSchin return(location);
931da2e3ebdSchin /* leading ^ means beginning of line unless escaped */
932da2e3ebdSchin if(flag)
933da2e3ebdSchin {
934da2e3ebdSchin index2 = *string;
935da2e3ebdSchin if(index2=='\\')
936da2e3ebdSchin string++;
937da2e3ebdSchin else if(index2=='^')
938da2e3ebdSchin {
939da2e3ebdSchin flag=0;
940da2e3ebdSchin string++;
941da2e3ebdSchin }
942da2e3ebdSchin }
943da2e3ebdSchin if(flag)
944da2e3ebdSchin coffset = &location.hist_char;
945da2e3ebdSchin index2 = (int)hp->histind;
946da2e3ebdSchin if(direction<0)
947da2e3ebdSchin {
948da2e3ebdSchin index2 -= hp->histsize;
949da2e3ebdSchin if(index2<1)
950da2e3ebdSchin index2 = 1;
951da2e3ebdSchin if(index1 <= index2)
952da2e3ebdSchin return(location);
953da2e3ebdSchin }
954da2e3ebdSchin else if(index1 >= index2)
955da2e3ebdSchin return(location);
956da2e3ebdSchin while(index1!=index2)
957da2e3ebdSchin {
958da2e3ebdSchin direction>0?++index1:--index1;
959da2e3ebdSchin offset = hist_tell(hp,index1);
960da2e3ebdSchin if((location.hist_line=hist_match(hp,offset,string,coffset))>=0)
961da2e3ebdSchin {
962da2e3ebdSchin location.hist_command = index1;
963da2e3ebdSchin return(location);
964da2e3ebdSchin }
965da2e3ebdSchin #if KSHELL
966da2e3ebdSchin /* allow a search to be aborted */
9677c2fbfb3SApril Chin if(((Shell_t*)hp->histshell)->trapnote&SH_SIGSET)
968da2e3ebdSchin break;
969da2e3ebdSchin #endif /* KSHELL */
970da2e3ebdSchin }
971da2e3ebdSchin return(location);
972da2e3ebdSchin }
973da2e3ebdSchin
974da2e3ebdSchin /*
975da2e3ebdSchin * search for <string> in history file starting at location <offset>
976da2e3ebdSchin * If coffset==0 then line must begin with string
977da2e3ebdSchin * returns the line number of the match if successful, otherwise -1
978da2e3ebdSchin */
979da2e3ebdSchin
hist_match(register History_t * hp,off_t offset,char * string,int * coffset)980da2e3ebdSchin int hist_match(register History_t *hp,off_t offset,char *string,int *coffset)
981da2e3ebdSchin {
982da2e3ebdSchin register unsigned char *first, *cp;
983da2e3ebdSchin register int m,n,c=1,line=0;
984da2e3ebdSchin #if SHOPT_MULTIBYTE
985da2e3ebdSchin mbinit();
986da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
987da2e3ebdSchin sfseek(hp->histfp,offset,SEEK_SET);
988da2e3ebdSchin if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0)))
989da2e3ebdSchin return(-1);
990da2e3ebdSchin m = sfvalue(hp->histfp);
991da2e3ebdSchin n = strlen(string);
992da2e3ebdSchin while(m > n)
993da2e3ebdSchin {
994da2e3ebdSchin if(*cp==*string && memcmp(cp,string,n)==0)
995da2e3ebdSchin {
996da2e3ebdSchin if(coffset)
997da2e3ebdSchin *coffset = (cp-first);
998da2e3ebdSchin return(line);
999da2e3ebdSchin }
1000da2e3ebdSchin if(!coffset)
1001da2e3ebdSchin break;
1002da2e3ebdSchin if(*cp=='\n')
1003da2e3ebdSchin line++;
1004da2e3ebdSchin #if SHOPT_MULTIBYTE
1005da2e3ebdSchin if((c=mbsize(cp)) < 0)
1006da2e3ebdSchin c = 1;
1007da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
1008da2e3ebdSchin cp += c;
1009da2e3ebdSchin m -= c;
1010da2e3ebdSchin }
1011da2e3ebdSchin return(-1);
1012da2e3ebdSchin }
1013da2e3ebdSchin
1014da2e3ebdSchin
1015da2e3ebdSchin #if SHOPT_ESH || SHOPT_VSH
1016da2e3ebdSchin /*
1017da2e3ebdSchin * copy command <command> from history file to s1
1018da2e3ebdSchin * at most <size> characters copied
1019da2e3ebdSchin * if s1==0 the number of lines for the command is returned
1020da2e3ebdSchin * line=linenumber for emacs copy and only this line of command will be copied
1021da2e3ebdSchin * line < 0 for full command copy
1022da2e3ebdSchin * -1 returned if there is no history file
1023da2e3ebdSchin */
1024da2e3ebdSchin
hist_copy(char * s1,int size,int command,int line)1025da2e3ebdSchin int hist_copy(char *s1,int size,int command,int line)
1026da2e3ebdSchin {
1027da2e3ebdSchin register int c;
1028da2e3ebdSchin register History_t *hp = sh_getinterp()->hist_ptr;
1029da2e3ebdSchin register int count = 0;
1030da2e3ebdSchin register char *s1max = s1+size;
1031da2e3ebdSchin if(!hp)
1032da2e3ebdSchin return(-1);
1033da2e3ebdSchin hist_seek(hp,command);
1034da2e3ebdSchin while ((c = sfgetc(hp->histfp)) && c!=EOF)
1035da2e3ebdSchin {
1036da2e3ebdSchin if(c=='\n')
1037da2e3ebdSchin {
1038da2e3ebdSchin if(count++ ==line)
1039da2e3ebdSchin break;
1040da2e3ebdSchin else if(line >= 0)
1041da2e3ebdSchin continue;
1042da2e3ebdSchin }
1043da2e3ebdSchin if(s1 && (line<0 || line==count))
1044da2e3ebdSchin {
1045da2e3ebdSchin if(s1 >= s1max)
1046da2e3ebdSchin {
1047da2e3ebdSchin *--s1 = 0;
1048da2e3ebdSchin break;
1049da2e3ebdSchin }
1050da2e3ebdSchin *s1++ = c;
1051da2e3ebdSchin }
1052da2e3ebdSchin
1053da2e3ebdSchin }
1054da2e3ebdSchin sfseek(hp->histfp,(off_t)0,SEEK_END);
1055da2e3ebdSchin if(s1==0)
1056da2e3ebdSchin return(count);
1057da2e3ebdSchin if(count && (c= *(s1-1)) == '\n')
1058da2e3ebdSchin s1--;
1059da2e3ebdSchin *s1 = '\0';
1060da2e3ebdSchin return(count);
1061da2e3ebdSchin }
1062da2e3ebdSchin
1063da2e3ebdSchin /*
1064da2e3ebdSchin * return word number <word> from command number <command>
1065da2e3ebdSchin */
1066da2e3ebdSchin
hist_word(char * string,int size,int word)1067da2e3ebdSchin char *hist_word(char *string,int size,int word)
1068da2e3ebdSchin {
1069da2e3ebdSchin register int c;
1070da2e3ebdSchin register char *s1 = string;
1071da2e3ebdSchin register unsigned char *cp = (unsigned char*)s1;
1072da2e3ebdSchin register int flag = 0;
1073da2e3ebdSchin History_t *hp = hist_ptr;
1074da2e3ebdSchin if(!hp)
1075da2e3ebdSchin return(NIL(char*));
1076da2e3ebdSchin hist_copy(string,size,(int)hp->histind-1,-1);
1077da2e3ebdSchin for(;c = *cp;cp++)
1078da2e3ebdSchin {
1079da2e3ebdSchin c = isspace(c);
1080da2e3ebdSchin if(c && flag)
1081da2e3ebdSchin {
1082da2e3ebdSchin *cp = 0;
1083da2e3ebdSchin if(--word==0)
1084da2e3ebdSchin break;
1085da2e3ebdSchin flag = 0;
1086da2e3ebdSchin }
1087da2e3ebdSchin else if(c==0 && flag==0)
1088da2e3ebdSchin {
1089da2e3ebdSchin s1 = (char*)cp;
1090da2e3ebdSchin flag++;
1091da2e3ebdSchin }
1092da2e3ebdSchin }
1093da2e3ebdSchin *cp = 0;
1094da2e3ebdSchin if(s1 != string)
1095da2e3ebdSchin strcpy(string,s1);
1096da2e3ebdSchin return(string);
1097da2e3ebdSchin }
1098da2e3ebdSchin
1099da2e3ebdSchin #endif /* SHOPT_ESH */
1100da2e3ebdSchin
1101da2e3ebdSchin #if SHOPT_ESH
1102da2e3ebdSchin /*
1103da2e3ebdSchin * given the current command and line number,
1104da2e3ebdSchin * and number of lines back or foward,
1105da2e3ebdSchin * compute the new command and line number.
1106da2e3ebdSchin */
1107da2e3ebdSchin
hist_locate(History_t * hp,register int command,register int line,int lines)1108da2e3ebdSchin Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines)
1109da2e3ebdSchin {
1110da2e3ebdSchin Histloc_t next;
1111da2e3ebdSchin line += lines;
1112da2e3ebdSchin if(!hp)
1113da2e3ebdSchin {
1114da2e3ebdSchin command = -1;
1115da2e3ebdSchin goto done;
1116da2e3ebdSchin }
1117da2e3ebdSchin if(lines > 0)
1118da2e3ebdSchin {
1119da2e3ebdSchin register int count;
1120da2e3ebdSchin while(command <= hp->histind)
1121da2e3ebdSchin {
1122da2e3ebdSchin count = hist_copy(NIL(char*),0, command,-1);
1123da2e3ebdSchin if(count > line)
1124da2e3ebdSchin goto done;
1125da2e3ebdSchin line -= count;
1126da2e3ebdSchin command++;
1127da2e3ebdSchin }
1128da2e3ebdSchin }
1129da2e3ebdSchin else
1130da2e3ebdSchin {
1131da2e3ebdSchin register int least = (int)hp->histind-hp->histsize;
1132da2e3ebdSchin while(1)
1133da2e3ebdSchin {
1134da2e3ebdSchin if(line >=0)
1135da2e3ebdSchin goto done;
1136da2e3ebdSchin if(--command < least)
1137da2e3ebdSchin break;
1138da2e3ebdSchin line += hist_copy(NIL(char*),0, command,-1);
1139da2e3ebdSchin }
1140da2e3ebdSchin command = -1;
1141da2e3ebdSchin }
1142da2e3ebdSchin done:
1143da2e3ebdSchin next.hist_line = line;
1144da2e3ebdSchin next.hist_command = command;
1145da2e3ebdSchin return(next);
1146da2e3ebdSchin }
1147da2e3ebdSchin #endif /* SHOPT_ESH */
1148da2e3ebdSchin
1149da2e3ebdSchin
1150da2e3ebdSchin /*
1151da2e3ebdSchin * Handle history file exceptions
1152da2e3ebdSchin */
1153da2e3ebdSchin #ifdef SF_BUFCONST
hist_exceptf(Sfio_t * fp,int type,void * data,Sfdisc_t * handle)1154da2e3ebdSchin static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle)
1155da2e3ebdSchin #else
1156da2e3ebdSchin static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle)
1157da2e3ebdSchin #endif
1158da2e3ebdSchin {
1159da2e3ebdSchin register int newfd,oldfd;
1160da2e3ebdSchin History_t *hp = (History_t*)handle;
1161da2e3ebdSchin if(type==SF_WRITE)
1162da2e3ebdSchin {
1163da2e3ebdSchin if(errno==ENOSPC || hp->histwfail++ >= 10)
1164da2e3ebdSchin return(0);
1165da2e3ebdSchin /* write failure could be NFS problem, try to re-open */
1166da2e3ebdSchin close(oldfd=sffileno(fp));
1167da2e3ebdSchin if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0)
1168da2e3ebdSchin {
1169da2e3ebdSchin if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd)
1170da2e3ebdSchin return(-1);
1171da2e3ebdSchin fcntl(oldfd,F_SETFD,FD_CLOEXEC);
1172da2e3ebdSchin close(newfd);
1173da2e3ebdSchin if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt)
1174da2e3ebdSchin {
1175da2e3ebdSchin register int index = hp->histind;
1176da2e3ebdSchin lseek(oldfd,(off_t)2,SEEK_SET);
1177da2e3ebdSchin hp->histcnt = 2;
1178da2e3ebdSchin hp->histind = 1;
1179da2e3ebdSchin hp->histcmds[1] = 2;
1180da2e3ebdSchin hist_eof(hp);
1181da2e3ebdSchin hp->histmarker = hp->histcnt;
1182da2e3ebdSchin hp->histind = index;
1183da2e3ebdSchin }
1184da2e3ebdSchin return(1);
1185da2e3ebdSchin }
1186da2e3ebdSchin errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname);
1187da2e3ebdSchin return(-1);
1188da2e3ebdSchin }
1189da2e3ebdSchin return(0);
1190da2e3ebdSchin }
1191