1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
22 * History file manipulation routines
30 * Each command in the history file starts on an even byte is null terminated.
31 * The first byte must contain the special character HIST_UNDO and the second
32 * byte is the version number. The sequence HIST_UNDO 0, following a command,
33 * nullifies the previous command. A six byte sequence starting with
34 * HIST_CMDNO is used to store the command number so that it is not necessary
35 * to read the file from beginning to end to get to the last block of
36 * commands. This format of this sequence is different in version 1
37 * then in version 0. Version 1 allows commands to use the full 8 bit
38 * character set. It can understand version 0 format files.
42 #define HIST_MAX (sizeof(int)*HIST_BSIZE)
43 #define HIST_BIG (0100000-1024) /* 1K less than maximum short */
44 #define HIST_LINE 32 /* typical length for history line */
46 #define HIST_RECENT 600
47 #define HIST_UNDO 0201 /* invalidate previous command */
48 #define HIST_CMDNO 0202 /* next 3 bytes give command number */
49 #define HIST_BSIZE 4096 /* size of history file buffer */
50 #define HIST_DFLT 512 /* default size of history list */
53 # define _HIST_AUDIT Sfio_t *auditfp; \
60 #define _HIST_PRIVATE \
62 off_t histcnt; /* offset into history file */\
63 off_t histmarker; /* offset of last command marker */ \
64 int histflush; /* set if flushed outside of hflush() */\
65 int histmask; /* power of two mask for histcnt */ \
66 char histbuff[HIST_BSIZE+1]; /* history file buffer */ \
69 off_t histcmds[2]; /* offset for recent commands, must be last */
71 #define hist_ind(hp,c) ((int)((c)&(hp)->histmask))
75 #include "FEATURE/time"
80 # include "variables.h"
82 # include "builtins.h"
90 # define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x)))
91 # define NIL(type) ((type)0)
92 # define path_relative(x) (x)
94 # define nv_getval(s) getenv(#s)
96 # define nv_getval(s) getenv("s")
97 # endif /* __STDC__ */
98 # define e_unknown "unknown"
99 # define sh_translate(x) (x)
101 char hist_fname
[] = "/.history";
106 #endif /* O_BINARY */
109 static void hist_marker(char*,long);
110 static History_t
* hist_trim(History_t
*, int);
111 static int hist_nearend(History_t
*,Sfio_t
*, off_t
);
112 static int hist_check(int);
113 static int hist_clean(int);
115 static ssize_t
hist_write(Sfio_t
*, const void*, size_t, Sfdisc_t
*);
116 static int hist_exceptf(Sfio_t
*, int, void*, Sfdisc_t
*);
118 static int hist_write(Sfio_t
*, const void*, int, Sfdisc_t
*);
119 static int hist_exceptf(Sfio_t
*, int, Sfdisc_t
*);
124 static mode_t histmode
;
125 static History_t
*wasopen
;
126 static History_t
*hist_ptr
;
130 static char *logname
;
133 static int acctinit(History_t
*hp
)
135 register char *cp
, *acctfile
;
136 Namval_t
*np
= nv_search("ACCTFILE",((Shell_t
*)hp
->histshell
)->var_tree
,0);
138 if(!np
|| !(acctfile
=nv_getval(np
)))
140 if(!(cp
= getlogin()))
142 struct passwd
*userinfo
= getpwuid(getuid());
144 cp
= userinfo
->pw_name
;
148 logname
= strdup(cp
);
149 if((acctfd
=sh_open(acctfile
,
150 O_BINARY
|O_WRONLY
|O_APPEND
|O_CREAT
,S_IRUSR
|S_IWUSR
))>=0 &&
151 (unsigned)acctfd
< 10)
154 if((n
= fcntl(acctfd
, F_DUPFD
, 10)) >= 0)
165 if(strmatch(acctfile
,e_devfdNN
))
168 sfsprintf(newfile
,sizeof(newfile
),"%.8s%d\0",e_devfdNN
,acctfd
);
169 nv_putval(np
,newfile
,NV_RDONLY
);
172 fcntl(acctfd
,F_SETFD
,FD_CLOEXEC
);
175 #endif /* SHOPT_ACCTFILE */
178 static int sh_checkaudit(History_t
*hp
, const char *name
, char *logbuf
, size_t len
)
180 Shell_t
*shp
= (Shell_t
*)hp
->histshell
;
181 char *buff
, *cp
, *last
;
182 int id1
, id2
, r
=0, n
, fd
;
183 if((fd
=open(name
, O_RDONLY
)) < 0)
185 if((n
= read(fd
, logbuf
,len
-1)) < 0)
187 while(logbuf
[n
-1]=='\n')
190 if(!(cp
=strchr(logbuf
,';')) && !(cp
=strchr(logbuf
,' ')))
196 id1
= id2
= strtol(cp
,&last
,10);
198 id1
= strtol(last
+1,&last
,10);
199 if(shp
->euserid
>=id1
&& shp
->euserid
<= id2
)
201 if(shp
->userid
>=id1
&& shp
->userid
<= id2
)
205 while(*cp
==';' || *cp
==' ');
211 #endif /*SHOPT_AUDIT*/
213 static const unsigned char hist_stamp
[2] = { HIST_UNDO
, HIST_VERSION
};
214 static const Sfdisc_t hist_disc
= { NULL
, hist_write
, NULL
, hist_exceptf
, NULL
};
216 static void hist_touch(void *handle
)
218 touch((char*)handle
, (time_t)0, (time_t)0, 0);
222 * open the history file
223 * if HISTNAME is not given and userid==0 then no history file.
224 * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is
226 * hist_open() returns 1, if history file is open
228 int sh_histinit(void *sh_context
)
230 Shell_t
*shp
= (Shell_t
*)sh_context
;
232 register History_t
*hp
;
233 register char *histname
;
235 int histmask
, maxlines
, hist_start
=0;
237 register off_t hsize
= 0;
239 if(shp
->hist_ptr
=hist_ptr
)
241 if(!(histname
= nv_getval(HISTFILE
)))
243 int offset
= staktell();
244 if(cp
=nv_getval(HOME
))
246 stakputs(hist_fname
);
249 histname
= stakptr(offset
);
254 /* reuse history file if same name */
256 shp
->hist_ptr
= hist_ptr
= hp
;
257 if(strcmp(histname
,hp
->histname
)==0)
264 cp
= path_relative(histname
);
266 histmode
= S_IRUSR
|S_IWUSR
;
267 if((fd
=open(cp
,O_BINARY
|O_APPEND
|O_RDWR
|O_CREAT
,histmode
))>=0)
269 hsize
=lseek(fd
,(off_t
)0,SEEK_END
);
274 if((n
=fcntl(fd
,F_DUPFD
,10))>=0)
280 /* make sure that file has history file format */
281 if(hsize
&& hist_check(fd
))
292 /* don't allow root a history_file in /tmp */
296 if(!(fname
= pathtmp(NIL(char*),0,0,NIL(int*))))
298 fd
= open(fname
,O_BINARY
|O_APPEND
|O_CREAT
|O_RDWR
,S_IRUSR
|S_IWUSR
);
303 /* set the file to close-on-exec */
304 fcntl(fd
,F_SETFD
,FD_CLOEXEC
);
305 if(cp
=nv_getval(HISTSIZE
))
306 maxlines
= (unsigned)strtol(cp
, (char**)0, 10);
308 maxlines
= HIST_DFLT
;
309 for(histmask
=16;histmask
<= maxlines
; histmask
<<=1 );
310 if(!(hp
=new_of(History_t
,(--histmask
)*sizeof(off_t
))))
315 shp
->hist_ptr
= hist_ptr
= hp
;
316 hp
->histshell
= (void*)shp
;
317 hp
->histsize
= maxlines
;
318 hp
->histmask
= histmask
;
319 hp
->histfp
= sfnew(NIL(Sfio_t
*),hp
->histbuff
,HIST_BSIZE
,fd
,SF_READ
|SF_WRITE
|SF_APPENDWR
|SF_SHARE
);
320 memset((char*)hp
->histcmds
,0,sizeof(off_t
)*(hp
->histmask
+1));
324 hp
->histname
= strdup(histname
);
325 hp
->histdisc
= hist_disc
;
328 /* put special characters at front of file */
329 sfwrite(hp
->histfp
,(char*)hist_stamp
,2);
332 /* initialize history list */
336 off_t mark
,size
= (HIST_MAX
/4)+maxlines
*HIST_LINE
;
337 hp
->histind
= first
= hist_nearend(hp
,hp
->histfp
,hsize
-size
);
338 hist_eof(hp
); /* this sets histind to last command */
339 if((hist_start
= (last
=(int)hp
->histind
)-maxlines
) <=0)
341 mark
= hp
->histmarker
;
342 while(first
> hist_start
)
345 first
= hist_nearend(hp
,hp
->histfp
,hsize
-size
);
348 histinit
= hist_start
;
352 sfseek(hp
->histfp
,hp
->histcnt
=hsize
,SEEK_SET
);
354 hp
->histmarker
= mark
;
363 if(hist_clean(fd
) && hist_start
>1 && hsize
> HIST_MAX
)
366 sfprintf(sfstderr
,"%d: hist_trim hsize=%d\n",getpid(),hsize
);
369 hp
= hist_trim(hp
,(int)hp
->histind
-maxlines
);
371 sfdisc(hp
->histfp
,&hp
->histdisc
);
373 (HISTCUR
)->nvalue
.lp
= (&hp
->histind
);
375 sh_timeradd(1000L*(HIST_RECENT
-30), 1, hist_touch
, (void*)hp
->histname
);
377 if(sh_isstate(SH_INTERACTIVE
))
379 #endif /* SHOPT_ACCTFILE */
382 char buff
[SF_BUFSIZE
];
384 if(sh_isstate(SH_INTERACTIVE
) && (hp
->auditmask
=sh_checkaudit(hp
,SHOPT_AUDITFILE
, buff
, sizeof(buff
))))
386 if((fd
=sh_open(buff
,O_BINARY
|O_WRONLY
|O_APPEND
|O_CREAT
,S_IRUSR
|S_IWUSR
))>=0 && fd
< 10)
389 if((n
= sh_fcntl(fd
,F_DUPFD
, 10)) >= 0)
397 hp
->tty
= strdup(ttyname(2));
398 hp
->auditfp
= sfnew((Sfio_t
*)0,NULL
,-1,fd
,SF_WRITE
);
407 * close the history file and free the space
410 void hist_close(register History_t
*hp
)
412 Shell_t
*shp
= (Shell_t
*)hp
->histshell
;
418 free((void*)hp
->tty
);
419 sfclose(hp
->auditfp
);
421 #endif /* SHOPT_AUDIT */
431 #endif /* SHOPT_ACCTFILE */
435 * check history file format to see if it begins with special byte
437 static int hist_check(register int fd
)
439 unsigned char magic
[2];
440 lseek(fd
,(off_t
)0,SEEK_SET
);
441 if((read(fd
,(char*)magic
,2)!=2) || (magic
[0]!=HIST_UNDO
))
447 * clean out history file OK if not modified in HIST_RECENT seconds
449 static int hist_clean(int fd
)
452 return(fstat(fd
,&statb
)>=0 && (time((time_t*)0)-statb
.st_mtime
) >= HIST_RECENT
);
456 * Copy the last <n> commands to a new file and make this the history file
459 static History_t
* hist_trim(History_t
*hp
, int n
)
462 register int incmd
=1, c
=0;
463 register History_t
*hist_new
, *hist_old
= hp
;
464 char *buff
, *endbuff
, *tmpname
=0;
467 unlink(hist_old
->histname
);
468 if(access(hist_old
->histname
,F_OK
) >= 0)
470 /* The unlink can fail on windows 95 */
472 char *last
, *name
=hist_old
->histname
;
473 close(sffileno(hist_old
->histfp
));
474 tmpname
= (char*)malloc(strlen(name
)+14);
475 if(last
= strrchr(name
,'/'))
478 pathtmp(tmpname
,name
,"hist",NIL(int*));
482 pathtmp(tmpname
,".","hist",NIL(int*));
483 if(rename(name
,tmpname
) < 0)
485 fd
= open(tmpname
,O_RDONLY
);
486 sfsetfd(hist_old
->histfp
,fd
);
491 if(fstat(sffileno(hist_old
->histfp
),&statb
)>=0)
494 histmode
= statb
.st_mode
;
496 if(!sh_histinit(hp
->histshell
))
498 /* use the old history file */
499 return hist_ptr
= hist_old
;
505 newp
= hist_seek(hist_old
,++n
);
510 c
= hist_ind(hist_new
,++hist_new
->histind
);
511 hist_new
->histcmds
[c
] = hist_new
->histcnt
;
512 if(hist_new
->histcnt
> hist_new
->histmarker
+HIST_BSIZE
/2)
514 char locbuff
[HIST_MARKSZ
];
515 hist_marker(locbuff
,hist_new
->histind
);
516 sfwrite(hist_new
->histfp
,locbuff
,HIST_MARKSZ
);
517 hist_new
->histcnt
+= HIST_MARKSZ
;
518 hist_new
->histmarker
= hist_new
->histcmds
[hist_ind(hist_new
,c
)] = hist_new
->histcnt
;
521 newp
= hist_seek(hist_old
,++n
);
525 if(!(buff
=(char*)sfreserve(hist_old
->histfp
,SF_UNBOUND
,0)))
527 *(endbuff
=(cp
=buff
)+sfvalue(hist_old
->histfp
)) = 0;
528 /* copy to null byte */
538 hist_new
->histcnt
+= c
;
539 sfwrite(hist_new
->histfp
,buff
,c
);
541 hist_cancel(hist_new
);
542 sfclose(hist_old
->histfp
);
548 free((char*)hist_old
);
549 return hist_ptr
= hist_new
;
553 * position history file at size and find next command number
555 static int hist_nearend(History_t
*hp
, Sfio_t
*iop
, register off_t size
)
557 register unsigned char *cp
, *endbuff
;
558 register int n
, incmd
=1;
559 unsigned char *buff
, marker
[4];
560 if(size
<= 2L || sfseek(iop
,size
,SEEK_SET
)<0)
562 /* skip to marker command and return the number */
563 /* numbering commands occur after a null and begin with HIST_CMDNO */
564 while(cp
=buff
=(unsigned char*)sfreserve(iop
,SF_UNBOUND
,SF_LOCKR
))
570 /* check for marker */
571 if(!incmd
&& *cp
++==HIST_CMDNO
&& *cp
==0)
584 if(*cp
==0 && ++cp
>endbuff
)
588 sfread(iop
,(char*)buff
,n
);
591 if((n
=sfread(iop
,(char*)marker
,4))==4)
593 n
= (marker
[0]<<16)|(marker
[1]<<8)|marker
[2];
596 hp
->histmarker
= hp
->histcnt
= size
+4;
607 sfseek(iop
,(off_t
)2,SEEK_SET
);
608 hp
->histmarker
= hp
->histcnt
= 2L;
613 * This routine reads the history file from the present position
614 * to the end-of-file and puts the information in the in-core
616 * Note that HIST_CMDNO is only recognized at the beginning of a command
617 * and that HIST_UNDO as the first character of a command is skipped
618 * unless it is followed by 0. If followed by 0 then it cancels
619 * the previous command.
622 void hist_eof(register History_t
*hp
)
624 register char *cp
,*first
,*endbuff
;
625 register int incmd
= 0;
626 register off_t count
= hp
->histcnt
;
628 sfseek(hp
->histfp
,count
,SEEK_SET
);
629 while(cp
=(char*)sfreserve(hp
->histfp
,SF_UNBOUND
,0))
631 n
= sfvalue(hp
->histfp
);
632 *(endbuff
= cp
+n
) = 0;
641 n
= hist_ind(hp
, ++hp
->histind
);
643 if(count
==hp
->histcmds
[n
])
645 sfprintf(sfstderr
,"count match n=%d\n",n
);
654 hp
->histcmds
[n
] = count
;
657 switch(*((unsigned char*)(cp
++)))
662 hp
->histmarker
=count
+2;
663 cp
+= (HIST_MARKSZ
-1);
668 unsigned char *marker
= (unsigned char*)(cp
-4);
669 int n
= ((marker
[0]<<16)
670 |(marker
[1]<<8)|marker
[2]);
671 if((n
<count
/2) && n
!= (hp
->histind
+1))
672 errormsg(SH_DICT
,2,"index=%d marker=%d", hp
->histind
, n
);
706 count
+= (--cp
-first
);
709 hp
->histcmds
[hist_ind(hp
,++hp
->histind
)] = count
;
715 * This routine will cause the previous command to be cancelled
718 void hist_cancel(register History_t
*hp
)
723 sfputc(hp
->histfp
,HIST_UNDO
);
724 sfputc(hp
->histfp
,0);
727 c
= hist_ind(hp
,--hp
->histind
);
728 hp
->histcmds
[c
] = hp
->histcnt
;
732 * flush the current history command
735 void hist_flush(register History_t
*hp
)
740 if(buff
=(char*)sfreserve(hp
->histfp
,0,SF_LOCKR
))
742 hp
->histflush
= sfvalue(hp
->histfp
)+1;
743 sfwrite(hp
->histfp
,buff
,0);
747 if(sfsync(hp
->histfp
)<0)
750 if(!sh_histinit(hp
->histshell
))
751 sh_offoption(SH_HISTORY
);
758 * This is the write discipline for the history file
759 * When called from hist_flush(), trailing newlines are deleted and
760 * a zero byte. Line sequencing is added as required
764 static ssize_t
hist_write(Sfio_t
*iop
,const void *buff
,register size_t insize
,Sfdisc_t
* handle
)
766 static int hist_write(Sfio_t
*iop
,const void *buff
,register int insize
,Sfdisc_t
* handle
)
769 register History_t
*hp
= (History_t
*)handle
;
770 register char *bufptr
= ((char*)buff
)+insize
;
771 register int c
,size
= insize
;
774 char saveptr
[HIST_MARKSZ
];
776 return(write(sffileno(iop
),(char*)buff
,size
));
777 if((cur
= lseek(sffileno(iop
),(off_t
)0,SEEK_END
)) <0)
779 errormsg(SH_DICT
,2,"hist_flush: EOF seek failed errno=%d",errno
);
783 /* remove whitespace from end of commands */
784 while(--bufptr
>= (char*)buff
)
789 if(c
=='\\' && *(bufptr
+1)!='\n')
794 /* don't count empty lines */
795 if(++bufptr
<= (char*)buff
)
799 size
= bufptr
- (char*)buff
;
803 Shell_t
*shp
= (Shell_t
*)hp
->histshell
;
804 time_t t
=time((time_t*)0);
805 sfprintf(hp
->auditfp
,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED
)?shp
->euserid
:shp
->userid
,t
,hp
->tty
,size
,buff
,0);
808 #endif /* SHOPT_AUDIT */
812 int timechars
, offset
;
815 stakseek(staktell() - 1);
816 timechars
= sfprintf(staksp
, "\t%s\t%x\n",logname
,time(NIL(long *)));
817 lseek(acctfd
, (off_t
)0, SEEK_END
);
818 write(acctfd
, stakptr(offset
), size
- 2 + timechars
);
822 #endif /* SHOPT_ACCTFILE */
829 c
= hist_ind(hp
,++hp
->histind
);
830 hp
->histcmds
[c
] = hp
->histcnt
;
831 if(hp
->histflush
>HIST_MARKSZ
&& hp
->histcnt
> hp
->histmarker
+HIST_BSIZE
/2)
833 memcpy((void*)saveptr
,(void*)bufptr
,HIST_MARKSZ
);
835 hp
->histcnt
+= HIST_MARKSZ
;
836 hist_marker(bufptr
,hp
->histind
);
837 hp
->histmarker
= hp
->histcmds
[hist_ind(hp
,c
)] = hp
->histcnt
;
841 size
= write(sffileno(iop
),(char*)buff
,size
);
843 memcpy((void*)bufptr
,(void*)saveptr
,HIST_MARKSZ
);
853 * Put history sequence number <n> into buffer <buff>
854 * The buffer must be large enough to hold HIST_MARKSZ chars
857 static void hist_marker(register char *buff
,register long cmdno
)
859 *buff
++ = HIST_CMDNO
;
861 *buff
++ = (cmdno
>>16);
862 *buff
++ = (cmdno
>>8);
868 * return byte offset in history file for command <n>
870 off_t
hist_tell(register History_t
*hp
, int n
)
872 return(hp
->histcmds
[hist_ind(hp
,n
)]);
876 * seek to the position of command <n>
878 off_t
hist_seek(register History_t
*hp
, int n
)
880 return(sfseek(hp
->histfp
,hp
->histcmds
[hist_ind(hp
,n
)],SEEK_SET
));
884 * write the command starting at offset <offset> onto file <outfile>.
885 * if character <last> appears before newline it is deleted
886 * each new-line character is replaced with string <nl>.
889 void hist_list(register History_t
*hp
,Sfio_t
*outfile
, off_t offset
,int last
, char *nl
)
895 sfputr(outfile
,sh_translate(e_unknown
),'\n');
898 sfseek(hp
->histfp
,offset
,SEEK_SET
);
899 while((c
= sfgetc(hp
->histfp
)) != EOF
)
902 sfputr(outfile
,nl
,-1);
903 else if(last
&& (c
==0 || (c
=='\n' && oldc
==last
)))
906 sfputc(outfile
,oldc
);
915 * find index for last line with given string
916 * If flag==0 then line must begin with string
917 * direction < 1 for backwards search
920 Histloc_t
hist_find(register History_t
*hp
,char *string
,register int index1
,int flag
,int direction
)
926 location
.hist_command
= -1;
927 location
.hist_char
= 0;
928 location
.hist_line
= 0;
931 /* leading ^ means beginning of line unless escaped */
944 coffset
= &location
.hist_char
;
945 index2
= (int)hp
->histind
;
948 index2
-= hp
->histsize
;
954 else if(index1
>= index2
)
956 while(index1
!=index2
)
958 direction
>0?++index1
:--index1
;
959 offset
= hist_tell(hp
,index1
);
960 if((location
.hist_line
=hist_match(hp
,offset
,string
,coffset
))>=0)
962 location
.hist_command
= index1
;
966 /* allow a search to be aborted */
967 if(((Shell_t
*)hp
->histshell
)->trapnote
&SH_SIGSET
)
975 * search for <string> in history file starting at location <offset>
976 * If coffset==0 then line must begin with string
977 * returns the line number of the match if successful, otherwise -1
980 int hist_match(register History_t
*hp
,off_t offset
,char *string
,int *coffset
)
982 register unsigned char *first
, *cp
;
983 register int m
,n
,c
=1,line
=0;
986 #endif /* SHOPT_MULTIBYTE */
987 sfseek(hp
->histfp
,offset
,SEEK_SET
);
988 if(!(cp
= first
= (unsigned char*)sfgetr(hp
->histfp
,0,0)))
990 m
= sfvalue(hp
->histfp
);
994 if(*cp
==*string
&& memcmp(cp
,string
,n
)==0)
997 *coffset
= (cp
-first
);
1005 if((c
=mbsize(cp
)) < 0)
1007 #endif /* SHOPT_MULTIBYTE */
1015 #if SHOPT_ESH || SHOPT_VSH
1017 * copy command <command> from history file to s1
1018 * at most <size> characters copied
1019 * if s1==0 the number of lines for the command is returned
1020 * line=linenumber for emacs copy and only this line of command will be copied
1021 * line < 0 for full command copy
1022 * -1 returned if there is no history file
1025 int hist_copy(char *s1
,int size
,int command
,int line
)
1028 register History_t
*hp
= sh_getinterp()->hist_ptr
;
1029 register int count
= 0;
1030 register char *s1max
= s1
+size
;
1033 hist_seek(hp
,command
);
1034 while ((c
= sfgetc(hp
->histfp
)) && c
!=EOF
)
1043 if(s1
&& (line
<0 || line
==count
))
1054 sfseek(hp
->histfp
,(off_t
)0,SEEK_END
);
1057 if(count
&& (c
= *(s1
-1)) == '\n')
1064 * return word number <word> from command number <command>
1067 char *hist_word(char *string
,int size
,int word
)
1070 register char *s1
= string
;
1071 register unsigned char *cp
= (unsigned char*)s1
;
1072 register int flag
= 0;
1073 History_t
*hp
= hist_ptr
;
1076 hist_copy(string
,size
,(int)hp
->histind
-1,-1);
1087 else if(c
==0 && flag
==0)
1099 #endif /* SHOPT_ESH */
1103 * given the current command and line number,
1104 * and number of lines back or foward,
1105 * compute the new command and line number.
1108 Histloc_t
hist_locate(History_t
*hp
,register int command
,register int line
,int lines
)
1120 while(command
<= hp
->histind
)
1122 count
= hist_copy(NIL(char*),0, command
,-1);
1131 register int least
= (int)hp
->histind
-hp
->histsize
;
1136 if(--command
< least
)
1138 line
+= hist_copy(NIL(char*),0, command
,-1);
1143 next
.hist_line
= line
;
1144 next
.hist_command
= command
;
1147 #endif /* SHOPT_ESH */
1151 * Handle history file exceptions
1154 static int hist_exceptf(Sfio_t
* fp
, int type
, void *data
, Sfdisc_t
*handle
)
1156 static int hist_exceptf(Sfio_t
* fp
, int type
, Sfdisc_t
*handle
)
1159 register int newfd
,oldfd
;
1160 History_t
*hp
= (History_t
*)handle
;
1163 if(errno
==ENOSPC
|| hp
->histwfail
++ >= 10)
1165 /* write failure could be NFS problem, try to re-open */
1166 close(oldfd
=sffileno(fp
));
1167 if((newfd
=open(hp
->histname
,O_BINARY
|O_APPEND
|O_CREAT
|O_RDWR
,S_IRUSR
|S_IWUSR
)) >= 0)
1169 if(fcntl(newfd
, F_DUPFD
, oldfd
) !=oldfd
)
1171 fcntl(oldfd
,F_SETFD
,FD_CLOEXEC
);
1173 if(lseek(oldfd
,(off_t
)0,SEEK_END
) < hp
->histcnt
)
1175 register int index
= hp
->histind
;
1176 lseek(oldfd
,(off_t
)2,SEEK_SET
);
1179 hp
->histcmds
[1] = 2;
1181 hp
->histmarker
= hp
->histcnt
;
1182 hp
->histind
= index
;
1186 errormsg(SH_DICT
,2,"History file write error-%d %s: file unrecoverable",errno
,hp
->histname
);