1 /* $XConsortium: history.c /main/4 1996/10/04 15:56:08 drk $ */
2 /***************************************************************
6 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF *
7 * AT&T BELL LABORATORIES *
8 * AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN *
9 * ACCORDANCE WITH APPLICABLE AGREEMENTS *
11 * Copyright (c) 1995 AT&T Corp. *
12 * Unpublished & Not for Publication *
13 * All Rights Reserved *
15 * The copyright notice above does not evidence any *
16 * actual or intended publication of such source code *
18 * This software was created by the *
19 * Advanced Software Technology Department *
20 * AT&T Bell Laboratories *
22 * For further information contact *
23 * {research,attmail}!dgk *
25 ***************************************************************/
27 /* : : generated by proto : : */
29 #if !defined(__PROTO__)
30 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
31 #if defined(__cplusplus)
32 #define __MANGLE__ "C"
37 #define __PROTO__(x) x
39 #define __PARAM__(n,o) n
40 #if !defined(__STDC__) && !defined(__cplusplus)
41 #if !defined(c_plusplus)
52 #define __PROTO__(x) ()
53 #define __OTORP__(x) x
54 #define __PARAM__(n,o) o
62 #if defined(__cplusplus) || defined(c_plusplus)
63 #define __VARARG__ ...
67 #if defined(__STDARG__)
68 #define __VA_START__(p,a) va_start(p,a)
70 #define __VA_START__(p,a) va_start(p)
73 #define HIST_MAX (sizeof(int)*HIST_BSIZE)
74 #define HIST_BIG (0100000-1024) /* 1K less than maximum short */
75 #define HIST_LINE 32 /* typical length for history line */
77 #define HIST_RECENT 600
78 #define HIST_UNDO 0201 /* invalidate previous command */
79 #define HIST_CMDNO 0202 /* next 3 bytes give command number */
80 #define HIST_BSIZE 1024 /* size of history file buffer */
81 #define HIST_DFLT 128 /* default size of history list */
83 #define _HIST_PRIVATE \
84 off_t histcnt; /* offset into history file */\
85 off_t histmarker; /* offset of last command marker */ \
86 int histflush; /* set if flushed outside of hflush() */\
87 int histmask; /* power of two mask for histcnt */ \
88 char histbuff[HIST_BSIZE+1]; /* history file buffer */ \
89 off_t histcmds[2]; /* byte offset for recent commands */
91 #define hist_ind(hp,c) ((int)((c)&(hp)->histmask))
95 #include "FEATURE/time"
101 # include "variables.h"
103 # include "builtins.h"
107 #include "national.h"
110 # define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x)))
111 # define NIL(type) ((type)0)
112 # define path_relative(x) (x)
114 #if defined(__STDC__) || defined(__STDPP__)
115 # define nv_getval(s) getenv(#s)
117 # define nv_getval(s) getenv("s")
120 # define nv_getval(s) getenv("s")
121 # endif /* __STDC__ */
122 # define e_unknown "unknown"
124 char hist_fname[] = "/.history";
129 #endif /* O_BINARY */
132 static void hist_marker __PROTO__((char*,long));
133 static void hist_trim __PROTO__((int));
134 static int hist_nearend __PROTO__((Sfio_t*, off_t));
135 static int hist_check __PROTO__((int));
136 static int hist_clean __PROTO__((int));
137 static int hist_write __PROTO__((Sfio_t*, const __V_*, int, Sfdisc_t*));
138 static int hist_exceptf __PROTO__((Sfio_t*, int, Sfdisc_t*));
139 static int histflush;
141 static mode_t histmode;
142 static int histwfail;
143 static History_t *wasopen;
144 static History_t *hist_ptr;
146 #ifdef SHOPT_ACCTFILE
148 static char *logname;
151 int acctinit __PARAM__((void), ()){
152 register char *cp, *acctfile;
153 Namval_t *np = nv_search("ACCTFILE",sh.var_tree,0);
155 if(!np || !(acctfile=nv_getval(np)))
157 if(!(cp = getlogin()))
159 struct passwd *userinfo = getpwuid(getuid());
161 cp = userinfo->pw_name;
165 logname = strdup(cp);
167 if((acctfd=sh_open(acctfile,
168 O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 &&
169 (unsigned)acctfd < 10)
172 if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0)
183 if(strmatch(acctfile,e_devfdNN))
186 sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd);
187 nv_putval(np,newfile,NV_RDONLY);
190 fcntl(acctfd,F_SETFD,FD_CLOEXEC);
193 #endif /* SHOPT_ACCTFILE */
195 static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION };
196 static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL};
198 static void hist_touch __PARAM__((__V_ *handle), (handle)) __OTORP__(__V_ *handle;){
199 touch((char*)handle, (time_t)0, (time_t)0, 0);
203 * open the history file
204 * if HISTNAME is not given and userid==0 then no history file.
205 * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is
207 * hist_open() returns 1, if history file is open
209 int sh_histinit __PARAM__((void), ()){
211 register History_t *hp;
212 register char *histname;
214 int histmask, maxlines, hist_start;
216 register off_t hsize = 0;
218 if(sh.hist_ptr=hist_ptr)
220 if(!(histname = nv_getval(HISTFILE)))
222 int offset = staktell();
223 if(cp=nv_getval(HOME))
225 stakputs(hist_fname);
228 histname = stakptr(offset);
233 /* reuse history file if same name */
235 sh.hist_ptr = hist_ptr = hp;
236 if(strcmp(histname,hp->histname)==0)
243 cp = path_relative(histname);
245 histmode = S_IRUSR|S_IWUSR;
246 if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0)
248 hsize=lseek(fd,(off_t)0,SEEK_END);
253 if((n=fcntl(fd,F_DUPFD,10))>=0)
259 /* make sure that file has history file format */
260 if(hsize && hist_check(fd))
271 /* don't allow root a history_file in /tmp */
275 if(!(fname = pathtemp(NIL(char*),0,0)))
277 fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
282 /* set the file to close-on-exec */
283 fcntl(fd,F_SETFD,FD_CLOEXEC);
284 if(cp=nv_getval(HISTSIZE))
285 maxlines = (unsigned)atoi(cp);
287 maxlines = HIST_DFLT;
288 for(histmask=16;histmask <= maxlines; histmask <<=1 );
289 if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t))))
294 sh.hist_ptr = hist_ptr = hp;
295 hp->histsize = maxlines;
296 hp->histmask = histmask;
297 hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPEND|SF_SHARE);
298 memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1));
302 hp->histname = strdup(histname);
303 hp->histdisc = hist_disc;
306 /* put special characters at front of file */
307 sfwrite(hp->histfp,(char*)hist_stamp,2);
310 /* initialize history list */
314 off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE;
315 hp->histind = first = hist_nearend(hp->histfp,hsize-size);
316 hist_eof(hp); /* this sets histind to last command */
317 if((hist_start = (last=(int)hp->histind)-maxlines) <=0)
319 mark = hp->histmarker;
320 while(first > hist_start)
323 first = hist_nearend(hp->histfp,hsize-size);
326 histinit = hist_start;
330 sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET);
332 hp->histmarker = mark;
341 if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX)
344 sfprintf(sfstderr,"%ld: hist_trim hsize=%ld\n",(long)getpid(),(long)hsize);
347 hist_trim((int)hp->histind-maxlines);
349 sfdisc(hp->histfp,&hp->histdisc);
351 (HISTCUR)->nvalue.lp = (&hp->histind);
353 timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (__V_*)hp->histname);
354 #ifdef SHOPT_ACCTFILE
355 if(sh_isstate(SH_INTERACTIVE))
357 #endif /* SHOPT_ACCTFILE */
362 * close the history file and free the space
365 void hist_close __PARAM__((register History_t *hp), (hp)) __OTORP__(register History_t *hp;){
370 #ifdef SHOPT_ACCTFILE
376 #endif /* SHOPT_ACCTFILE */
380 * check history file format to see if it begins with special byte
382 static int hist_check __PARAM__((register int fd), (fd)) __OTORP__(register int fd;){
383 unsigned char magic[2];
384 lseek(fd,(off_t)0,SEEK_SET);
385 if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO))
391 * clean out history file OK if not modified in HIST_RECENT seconds
393 static int hist_clean __PARAM__((int fd), (fd)) __OTORP__(int fd;){
395 return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT);
399 * Copy the last <n> commands to a new file and make this the history file
402 static void hist_trim __PARAM__((int n), (n)) __OTORP__(int n;){
404 register int incmd=1, c=0;
405 register History_t *hist_new, *hist_old = hist_ptr;
406 char *buff, *endbuff;
409 unlink(hist_old->histname);
411 if(fstat(sffileno(hist_old->histfp),&statb)>=0)
414 histmode = statb.st_mode;
418 /* use the old history file */
426 newp = hist_seek(hist_old,++n);
431 c = hist_ind(hist_new,++hist_new->histind);
432 hist_new->histcmds[c] = hist_new->histcnt;
433 if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2)
435 char locbuff[HIST_MARKSZ];
436 hist_marker(locbuff,hist_new->histind);
437 sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ);
438 hist_new->histcnt += HIST_MARKSZ;
439 hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt;
442 newp = hist_seek(hist_old,++n);
446 if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0)))
448 *(endbuff=(cp=buff)+sfslen()) = 0;
449 /* copy to null byte */
459 hist_new->histcnt += c;
460 sfwrite(hist_new->histfp,buff,c);
463 hist_cancel(hist_ptr);
464 sfclose(hist_old->histfp);
465 free((char*)hist_old);
469 * position history file at size and find next command number
471 static int hist_nearend __PARAM__((Sfio_t *iop, register off_t size), (iop, size)) __OTORP__(Sfio_t *iop; register off_t size;){
472 register unsigned char *cp, *endbuff;
473 register int n, incmd=1;
474 unsigned char *buff, marker[4];
475 if(size <= 2L || sfseek(iop,size,SEEK_SET)<0)
477 /* skip to marker command and return the number */
478 /* numbering commands occur after a null and begin with HIST_CMDNO */
479 while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,1))
485 /* check for marker */
486 if(!incmd && *cp++==HIST_CMDNO && *cp==0)
499 if(*cp==0 && ++cp>endbuff)
503 sfread(iop,(char*)buff,n);
506 if((n=sfread(iop,(char*)marker,4))==4)
508 n = (marker[0]<<16)|(marker[1]<<8)|marker[2];
511 hist_ptr->histmarker = hist_ptr->histcnt = size+4;
522 sfseek(iop,(off_t)2,SEEK_SET);
523 hist_ptr->histmarker = hist_ptr->histcnt = 2L;
528 * This routine reads the history file from the present position
529 * to the end-of-file and puts the information in the in-core
531 * Note that HIST_CMDNO is only recognized at the beginning of a command
532 * and that HIST_UNDO as the first character of a command is skipped
533 * unless it is followed by 0. If followed by 0 then it cancels
534 * the previous command.
537 void hist_eof __PARAM__((register History_t *hp), (hp)) __OTORP__(register History_t *hp;){
538 register char *cp,*first,*endbuff;
539 register int incmd = 0;
540 register off_t count = hp->histcnt;
543 sfseek(hp->histfp,count,SEEK_SET);
544 while(cp=buff=(char*)sfreserve(hp->histfp,SF_UNBOUND,0))
547 *(endbuff = cp+n) = 0;
556 n = hist_ind(hp, ++hp->histind);
558 if(count==hp->histcmds[n])
560 sfprintf(sfstderr,"count match n=%d\n",n);
569 hp->histcmds[n] = count;
572 switch(*((unsigned char*)(cp++)))
577 hp->histmarker=count+2;
578 cp += (HIST_MARKSZ-1);
583 unsigned char *marker = (unsigned char*)(cp-4);
584 int n = ((marker[0]<<16)
585 |(marker[1]<<8)|marker[2]);
586 if((n<count/2) && n != (hp->histind+1))
587 error(2,"index=%d marker=%d", hp->histind, n);
621 count += (--cp-first);
624 hp->histcmds[hist_ind(hp,++hp->histind)] = count;
630 * This routine will cause the previous command to be cancelled
633 void hist_cancel __PARAM__((register History_t *hp), (hp)) __OTORP__(register History_t *hp;){
637 sfputc(hp->histfp,HIST_UNDO);
638 sfputc(hp->histfp,0);
641 c = hist_ind(hp,--hp->histind);
642 hp->histcmds[c] = hp->histcnt;
646 * flush the current history command
649 void hist_flush __PARAM__((register History_t *hp), (hp)) __OTORP__(register History_t *hp;){
653 if(buff=(char*)sfreserve(hp->histfp,0,1))
655 histflush = sfslen()+1;
656 sfwrite(hp->histfp,buff,0);
660 if(sfsync(hp->histfp)<0)
664 sh_offoption(SH_HISTORY);
671 * This is the write discipline for the history file
672 * When called from hist_flush(), trailing newlines are deleted and
673 * a zero byte. Line sequencing is added as required
676 static int hist_write __PARAM__((Sfio_t *iop,const __V_ *buff,register int insize,Sfdisc_t* handle), (iop, buff, insize, handle)) __OTORP__(Sfio_t *iop;const __V_ *buff;register int insize;Sfdisc_t* handle;){
677 register char *bufptr = ((char*)buff)+insize;
678 register History_t *hp = hist_ptr;
679 register int c,size = insize;
683 return(write(sffileno(iop),(char*)buff,size));
684 if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0)
686 error(2,"hist_flush: EOF seek failed errno=%d",errno);
690 /* remove whitespace from end of commands */
691 while(--bufptr >= (char*)buff)
696 if(c=='\\' && *(bufptr+1)!='\n')
701 /* don't count empty lines */
702 if(++bufptr <= (char*)buff)
706 size = bufptr - (char*)buff;
707 #ifdef SHOPT_ACCTFILE
710 int timechars, offset;
713 stakseek(staktell() - 1);
714 timechars = sfprintf(staksp, "\t%s\t%lx\n",logname,(long)time(NIL(long *)));
715 lseek(acctfd, (off_t)0, SEEK_END);
716 write(acctfd, stakptr(offset), size - 2 + timechars);
720 #endif /* SHOPT_ACCTFILE */
727 c = hist_ind(hp,++hp->histind);
728 hp->histcmds[c] = hp->histcnt;
729 if(histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2)
731 hp->histcnt += HIST_MARKSZ;
732 hist_marker(bufptr,hp->histind);
733 hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt;
737 if(write(sffileno(iop),(char*)buff,size)>=0)
746 * Put history sequence number <n> into buffer <buff>
747 * The buffer must be large enough to hold HIST_MARKSZ chars
750 static void hist_marker __PARAM__((register char *buff,register long cmdno), (buff, cmdno)) __OTORP__(register char *buff;register long cmdno;){
751 *buff++ = HIST_CMDNO;
753 *buff++ = (cmdno>>16);
754 *buff++ = (cmdno>>8);
760 * return byte offset in history file for command <n>
762 off_t hist_tell __PARAM__((register History_t *hp, int n), (hp, n)) __OTORP__(register History_t *hp; int n;){
763 return(hp->histcmds[hist_ind(hp,n)]);
767 * seek to the position of command <n>
769 off_t hist_seek __PARAM__((register History_t *hp, int n), (hp, n)) __OTORP__(register History_t *hp; int n;){
770 return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET));
774 * write the command starting at offset <offset> onto file <outfile>.
775 * if character <last> appears before newline it is deleted
776 * each new-line character is replaced with string <nl>.
779 void hist_list __PARAM__((register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl), (hp, outfile, offset, last, nl)) __OTORP__(register History_t *hp;Sfio_t *outfile; off_t offset;int last; char *nl;){
784 sfputr(outfile,e_unknown,'\n');
787 sfseek(hp->histfp,offset,SEEK_SET);
788 while((c = sfgetc(hp->histfp)) != EOF)
791 sfputr(outfile,nl,-1);
792 else if(last && (c==0 || (c=='\n' && oldc==last)))
795 sfputc(outfile,oldc);
804 * find index for last line with given string
805 * If flag==0 then line must begin with string
806 * direction < 1 for backwards search
809 Histloc_t hist_find __PARAM__((register History_t*hp,char *string,register int index1,int flag,int direction), (hp, string, index1, flag, direction)) __OTORP__(register History_t*hp;char *string;register int index1;int flag;int direction;){
814 location.hist_command = -1;
815 location.hist_char = 0;
818 /* leading ^ means beginning of line unless escaped */
831 coffset = &location.hist_char;
832 index2 = (int)hp->histind;
835 index2 -= hp->histsize;
841 else if(index1 >= index2)
843 while(index1!=index2)
845 direction>0?++index1:--index1;
846 offset = hist_tell(hp,index1);
847 if((location.hist_line=hist_match(hp,offset,string,coffset))>=0)
849 location.hist_command = index1;
853 /* allow a search to be aborted */
854 if(sh.trapnote&SH_SIGSET)
862 * search for <string> in history file starting at location <offset>
863 * If coffset==0 then line must begin with string
864 * returns the line number of the match if successful, otherwise -1
867 int hist_match __PARAM__((register History_t *hp,off_t offset,char *string,int *coffset), (hp, offset, string, coffset)) __OTORP__(register History_t *hp;off_t offset;char *string;int *coffset;){
868 register unsigned char *cp;
870 register off_t count;
873 #ifdef SHOPT_MULTIBYTE
875 #endif /* SHOPT_MULTIBYTE */
880 sfseek(hp->histfp,offset,SEEK_SET);
881 #ifdef SHOPT_MULTIBYTE
882 mblen(NIL(char*),MB_CUR_MAX);
884 #endif /* SHOPT_MULTIBYTE */
888 for(cp=(unsigned char*)string;*cp;cp++)
890 if((c=sfgetc(hp->histfp)) == EOF || c ==0)
893 #ifdef SHOPT_MULTIBYTE
894 /* always position at character boundary */
897 if(cp==(unsigned char*)string)
903 else if((nbytes = mblen((char*)cp,MB_CUR_MAX)) <=0)
905 #endif /* SHOPT_MULTIBYTE */
908 /* save earliest possible matching character */
909 if(coffset && c == *(unsigned char*)string && offset<0)
911 #ifdef SHOPT_MULTIBYTE
912 offset = count + (nbytes - 1);
915 #endif /* SHOPT_MULTIBYTE */
921 if(*cp==0) /* match found */
925 while(coffset && c && c != EOF);
930 #if SHOPT_ESH || SHOPT_VSH
932 * copy command <command> from history file to s1
933 * at most <size> characters copied
934 * if s1==0 the number of lines for the command is returned
935 * line=linenumber for emacs copy and only this line of command will be copied
936 * line < 0 for full command copy
937 * -1 returned if there is no history file
940 int hist_copy __PARAM__((char *s1,int size,int command,int line), (s1, size, command, line)) __OTORP__(char *s1;int size;int command;int line;){
942 register History_t *hp = hist_ptr;
943 register int count = 0;
944 register char *s1max = s1+size;
948 offset = hist_seek(hp,command);
949 while ((c = sfgetc(hp->histfp)) && c!=EOF)
958 if(s1 && (line<0 || line==count))
969 sfseek(hp->histfp,(off_t)0,SEEK_END);
972 if(count && (c= *(s1-1)) == '\n')
979 * return word number <word> from command number <command>
982 char *hist_word __PARAM__((char *string,int size,int word), (string, size, word)) __OTORP__(char *string;int size;int word;){
984 register char *s1 = string;
985 register unsigned char *cp = (unsigned char*)s1;
986 register int flag = 0;
990 strncpy(string,sh.lastarg,size);
996 hist_copy(string,size,(int)hist_ptr->histind-1,-1);
1007 else if(c==0 && flag==0)
1019 #endif /* SHOPT_ESH */
1023 * given the current command and line number,
1024 * and number of lines back or foward,
1025 * compute the new command and line number.
1028 Histloc_t hist_locate __PARAM__((History_t *hp,register int command,register int line,int lines), (hp, command, line, lines)) __OTORP__(History_t *hp;register int command;register int line;int lines;){
1039 while(command <= hp->histind)
1041 count = hist_copy(NIL(char*),0, command,-1);
1050 register int least = (int)hp->histind-hp->histsize;
1055 if(--command < least)
1057 line += hist_copy(NIL(char*),0, command,-1);
1061 next.hist_command = command;
1064 next.hist_line = line;
1065 next.hist_command = command;
1068 #endif /* SHOPT_ESH */
1072 * Handle history file exceptions
1074 static int hist_exceptf __PARAM__((Sfio_t* fp, int type, Sfdisc_t *handle), (fp, type, handle)) __OTORP__(Sfio_t* fp; int type; Sfdisc_t *handle;){
1075 register int newfd,oldfd;
1076 History_t *hp = (History_t*)handle;
1079 if(errno==ENOSPC || histwfail++ >= 10)
1081 /* write failure could be NFS problem, try to re-open */
1082 close(oldfd=sffileno(fp));
1083 if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0)
1085 if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd)
1087 fcntl(oldfd,F_SETFD,FD_CLOEXEC);
1089 if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt)
1091 register int index = hp->histind;
1092 lseek(oldfd,(off_t)2,SEEK_SET);
1095 hp->histcmds[1] = 2;
1097 hp->histmarker = hp->histcnt;
1098 hp->histind = index;
1102 error(2,"History file write error-%d %s: file unrecoverable",errno,hp->histname);