1 /* $OpenBSD: history.c,v 1.15 2002/10/23 14:23:15 millert Exp $ */
6 * only implements in-memory history.
11 * a) the original in-memory history mechanism
12 * b) a simple file saving history mechanism done by sjg@zen
13 * define EASY_HISTORY to get this
14 * c) a more complicated mechanism done by pc@hillside.co.uk
15 * that more closely follows the real ksh way of doing
16 * things. You need to have the mmap system call for this
17 * to work on your system
28 # define HISTFILE "history.ksh"
30 # define HISTFILE ".pdksh_history"
35 /* Defines and includes for the complicated case */
37 # include <sys/file.h>
38 # include <sys/mman.h>
41 * variables for handling the data file
46 static int hist_count_lines
ARGS((unsigned char *, int));
47 static int hist_shrink
ARGS((unsigned char *, int));
48 static unsigned char *hist_skip_back
ARGS((unsigned char *,int *,int));
49 static void histload
ARGS((Source
*, unsigned char *, int));
50 static void histinsert
ARGS((Source
*, int, unsigned char *));
51 static void writehistfile
ARGS((int, char *));
52 static int sprinkle
ARGS((int));
55 # define MAP_FLAGS (MAP_FILE|MAP_PRIVATE)
57 # define MAP_FLAGS MAP_PRIVATE
60 # endif /* of EASY_HISTORY */
62 static int hist_execute
ARGS((char *cmd
));
63 static int hist_replace
ARGS((char **hp
, const char *pat
, const char *rep
,
65 static char **hist_get
ARGS((const char *str
, int approx
, int allow_cur
));
66 static char **hist_get_newest
ARGS((int allow_cur
));
67 static char **hist_get_oldest
ARGS(());
68 static void histbackup
ARGS((void));
70 static char **current
; /* current postition in history[] */
71 static int curpos
; /* current index in history[] */
72 static char *hname
; /* current name of history file */
73 static int hstarted
; /* set after hist_init() called */
74 static Source
*hist_source
;
82 struct temp
UNINITIALIZED(*tf
);
83 char *p
, *editor
= (char *) 0;
84 int gflag
= 0, lflag
= 0, nflag
= 0, sflag
= 0, rflag
= 0;
86 char *first
= (char *) 0, *last
= (char *) 0;
87 char **hfirst
, **hlast
, **hp
;
89 while ((optc
= ksh_getopt(wp
, &builtin_opt
, "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != EOF
)
92 p
= builtin_opt
.optarg
;
93 if (strcmp(p
, "-") == 0)
96 editor
= str_nsave(p
, strlen(p
) + 4, ATEMP
);
97 strcat(editor
, " $_");
100 case 'g': /* non-at&t ksh */
112 case 's': /* posix version of -e - */
115 /* kludge city - accept -num as -- -num (kind of) */
116 case '0': case '1': case '2': case '3': case '4':
117 case '5': case '6': case '7': case '8': case '9':
118 p
= shf_smprintf("-%c%s",
119 optc
, builtin_opt
.optarg
);
125 bi_errorf("too many arguments");
132 wp
+= builtin_opt
.optind
;
134 /* Substitute and execute command */
136 char *pat
= (char *) 0, *rep
= (char *) 0;
138 if (editor
|| lflag
|| nflag
|| rflag
) {
139 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
143 /* Check for pattern replacement argument */
144 if (*wp
&& **wp
&& (p
= strchr(*wp
+ 1, '='))) {
145 pat
= str_save(*wp
, ATEMP
);
151 /* Check for search prefix */
152 if (!first
&& (first
= *wp
))
155 bi_errorf("too many arguments");
159 hp
= first
? hist_get(first
, FALSE
, FALSE
)
160 : hist_get_newest(FALSE
);
163 return hist_replace(hp
, pat
, rep
, gflag
);
166 if (editor
&& (lflag
|| nflag
)) {
167 bi_errorf("can't use -l, -n with -e");
171 if (!first
&& (first
= *wp
))
173 if (!last
&& (last
= *wp
))
176 bi_errorf("too many arguments");
180 hfirst
= lflag
? hist_get("-16", TRUE
, TRUE
)
181 : hist_get_newest(FALSE
);
184 /* can't fail if hfirst didn't fail */
185 hlast
= hist_get_newest(FALSE
);
187 /* POSIX says not an error if first/last out of bounds
188 * when range is specified; at&t ksh and pdksh allow out of
189 * bounds for -l as well.
191 hfirst
= hist_get(first
, (lflag
|| last
) ? TRUE
: FALSE
,
192 lflag
? TRUE
: FALSE
);
195 hlast
= last
? hist_get(last
, TRUE
, lflag
? TRUE
: FALSE
)
196 : (lflag
? hist_get_newest(FALSE
) : hfirst
);
200 if (hfirst
> hlast
) {
203 temp
= hfirst
; hfirst
= hlast
; hlast
= temp
;
204 rflag
= !rflag
; /* POSIX */
210 const char *nfmt
= nflag
? "\t" : "%d\t";
212 for (hp
= rflag
? hlast
: hfirst
;
213 hp
>= hfirst
&& hp
<= hlast
; hp
+= rflag
? -1 : 1)
215 shf_fprintf(shl_stdout
, nfmt
,
216 hist_source
->line
- (int) (histptr
- hp
));
217 /* print multi-line commands correctly */
218 for (s
= *hp
; (t
= strchr(s
, '\n')); s
= t
)
219 shf_fprintf(shl_stdout
, "%.*s\t", ++t
- s
, s
);
220 shf_fprintf(shl_stdout
, "%s\n", s
);
222 shf_flush(shl_stdout
);
226 /* Run editor on selected lines, then run resulting commands */
228 tf
= maketemp(ATEMP
, TT_HIST_EDIT
, &e
->temps
);
229 if (!(shf
= tf
->shf
)) {
230 bi_errorf("cannot create temp file %s - %s",
231 tf
->name
, strerror(errno
));
234 for (hp
= rflag
? hlast
: hfirst
;
235 hp
>= hfirst
&& hp
<= hlast
; hp
+= rflag
? -1 : 1)
236 shf_fprintf(shf
, "%s\n", *hp
);
237 if (shf_close(shf
) == EOF
) {
238 bi_errorf("error writing temporary file - %s", strerror(errno
));
244 /* Ignore setstr errors here (arbitrary) */
245 /* setstr(local("_", FALSE), tf->name, KSH_RETURN_ERROR);
249 /* XXX: source should not get trashed by this.. */
251 Source
*sold
= source
;
254 ret
= command(editor
? editor
: "${FCEDIT:-/bin/ed} $_");
266 if (!(shf
= shf_open(tf
->name
, O_RDONLY
, 0, 0))) {
267 bi_errorf("cannot open temp file %s", tf
->name
);
271 n
= fstat(shf_fileno(shf
), &statb
) < 0 ? 128
273 Xinit(xs
, xp
, n
, hist_source
->areap
);
274 while ((n
= shf_read(xp
, Xnleft(xs
, xp
), shf
)) > 0) {
276 if (Xnleft(xs
, xp
) <= 0)
277 XcheckN(xs
, xp
, Xlength(xs
, xp
));
280 bi_errorf("error reading temp file %s - %s",
281 tf
->name
, strerror(shf_errno(shf
)));
287 strip_nuls(Xstring(xs
, xp
), Xlength(xs
, xp
));
288 return hist_execute(Xstring(xs
, xp
));
292 /* Save cmd in history, execute cmd (cmd gets trashed) */
303 for (p
= cmd
; p
; p
= q
) {
304 if ((q
= strchr(p
, '\n'))) {
305 *q
++ = '\0'; /* kill the newline */
306 if (!*q
) /* ignore trailing newline */
313 #endif /* EASY_HISTORY */
314 histsave(++(hist_source
->line
), p
, 1);
316 shellf("%s\n", p
); /* POSIX doesn't say this is done... */
317 if ((p
= q
)) /* restore \n (trailing \n not restored) */
321 /* Commands are executed here instead of pushing them onto the
322 * input 'cause posix says the redirection and variable assignments
324 * X=y fc -e - 42 2> /dev/null
325 * are to effect the repeated commands environment.
327 /* XXX: source should not get trashed by this.. */
335 hist_replace(hp
, pat
, rep
, global
)
344 line
= str_save(*hp
, ATEMP
);
347 int pat_len
= strlen(pat
);
348 int rep_len
= strlen(rep
);
354 Xinit(xs
, xp
, 128, ATEMP
);
355 for (s
= *hp
; (s1
= strstr(s
, pat
))
356 && (!any_subst
|| global
) ; s
= s1
+ pat_len
)
360 XcheckN(xs
, xp
, len
+ rep_len
);
361 memcpy(xp
, s
, len
); /* first part */
363 memcpy(xp
, rep
, rep_len
); /* replacement */
367 bi_errorf("substitution failed");
371 XcheckN(xs
, xp
, len
);
374 line
= Xclose(xs
, xp
);
376 return hist_execute(line
);
380 * get pointer to history given pattern
381 * pattern is a number or string
384 hist_get(str
, approx
, allow_cur
)
389 char **hp
= (char **) 0;
393 hp
= histptr
+ (n
< 0 ? n
: (n
- hist_source
->line
));
396 hp
= hist_get_oldest();
398 bi_errorf("%s: not in history", str
);
401 } else if (hp
> histptr
) {
403 hp
= hist_get_newest(allow_cur
);
405 bi_errorf("%s: not in history", str
);
408 } else if (!allow_cur
&& hp
== histptr
) {
409 bi_errorf("%s: invalid range", str
);
413 int anchored
= *str
== '?' ? (++str
, 0) : 1;
415 /* the -1 is to avoid the current fc command */
416 n
= findhist(histptr
- history
- 1, 0, str
, anchored
);
418 bi_errorf("%s: not in history", str
);
426 /* Return a pointer to the newest command in the history */
428 hist_get_newest(allow_cur
)
431 if (histptr
< history
|| (!allow_cur
&& histptr
== history
)) {
432 bi_errorf("no history (yet)");
440 /* Return a pointer to the newest command in the history */
444 if (histptr
<= history
) {
445 bi_errorf("no history (yet)");
451 /******************************/
452 /* Back up over last histsave */
453 /******************************/
457 static int last_line
= -1;
459 if (histptr
>= history
&& last_line
!= hist_source
->line
) {
461 afree((void*)*histptr
, APERM
);
463 last_line
= hist_source
->line
;
468 * Return the current position.
486 int last
= histptr
- history
;
488 if (n
< 0 || n
>= last
) {
493 current
= &history
[n
];
500 * This will become unecessary if hist_get is modified to allow
501 * searching from positions other than the end, and in either
505 findhist(start
, fwd
, str
, anchored
)
512 int maxhist
= histptr
- history
;
513 int incr
= fwd
? 1 : -1;
514 int len
= strlen(str
);
516 if (start
< 0 || start
>= maxhist
)
519 hp
= &history
[start
];
520 for (; hp
>= history
&& hp
<= histptr
; hp
+= incr
)
521 if ((anchored
&& strncmp(*hp
, str
, len
) == 0)
522 || (!anchored
&& strstr(*hp
, str
)))
530 * this means reallocating the dataspace
536 if (n
> 0 && n
!= histsize
) {
537 int cursize
= histptr
- history
;
539 /* save most recent history */
541 memmove(history
, histptr
- n
, n
* sizeof(char *));
545 history
= (char **)aresize(history
, n
*sizeof(char *), APERM
);
548 histptr
= history
+ cursize
;
554 * This can mean reloading/resetting/starting history file
561 /* if not started then nothing to do */
565 /* if the name is the same as the name we have */
566 if (hname
&& strcmp(hname
, name
) == 0)
570 * its a new name - possibly
579 /* yes the file is open */
580 (void) close(histfd
);
585 /* let's reset the history */
586 histptr
= history
- 1;
587 hist_source
->line
= 0;
591 hist_init(hist_source
);
595 * initialise the history vector
600 if (history
== (char **)NULL
) {
601 histsize
= HISTORYSIZE
;
602 history
= (char **)alloc(histsize
*sizeof (char *), APERM
);
603 histptr
= history
- 1;
609 * save command in history
612 histsave(lno
, cmd
, dowrite
)
613 int lno
; /* ignored (compatibility with COMPLEX_HISTORY) */
615 int dowrite
; /* ignored (compatibility with COMPLEX_HISTORY) */
617 register char **hp
= histptr
;
620 if (++hp
>= history
+ histsize
) { /* remove oldest command */
621 afree((void*)history
[0], APERM
);
622 memmove(history
, history
+ 1,
623 sizeof(history
[0]) * (histsize
- 1));
624 hp
= &history
[histsize
- 1];
626 *hp
= str_save(cmd
, APERM
);
627 /* trash trailing newline but allow imbedded newlines */
628 cp
= *hp
+ strlen(*hp
);
629 if (cp
> *hp
&& cp
[-1] == '\n')
635 * Append an entry to the last saved command. Used for multiline
639 histappend(cmd
, nl_separate
)
646 hlen
= strlen(*histptr
);
648 if (clen
> 0 && cmd
[clen
-1] == '\n')
650 p
= *histptr
= (char *) aresize(*histptr
, hlen
+ clen
+ 2, APERM
);
654 memcpy(p
, cmd
, clen
);
660 * A simple history file implementation.
661 * At present we only save the history when we exit.
662 * This can cause problems when there are multiple shells are
663 * running under the same user-id. The last shell to exit gets
664 * to save its history.
673 if (Flag(FTALKING
) == 0)
680 if ((f
= str_val(global("HISTFILE"))) == NULL
|| *f
== '\0') {
681 # if 1 /* Don't use history file unless the user asks for it */
685 char *home
= str_val(global("HOME"));
691 hname
= alloc(len
= strlen(home
) + strlen(f
) + 2, APERM
);
692 shf_snprintf(hname
, len
, "%s/%s", home
, f
);
695 hname
= str_save(f
, APERM
);
697 if ((fh
= fopen(hname
, "r"))) {
698 int pos
= 0, nread
= 0;
699 int contin
= 0; /* continuation of previous command */
701 char hline
[LINE
+ 1];
706 nread
= fread(hline
, 1, LINE
, fh
);
711 end
= strchr(hline
+ pos
, 0); /* will always succeed */
713 histappend(hline
+ pos
, 0);
716 histsave(0, hline
+ pos
, 0);
718 pos
= end
- hline
+ 1;
719 contin
= end
== &hline
[nread
];
727 * We check that we do not have more than we are allowed.
728 * If the history file is read-only we do nothing.
729 * Handy for having all shells start with a useful history set.
742 /* check how many we have */
743 i
= histptr
- history
;
745 hp
= &histptr
[-histsize
];
748 if (hname
&& (fh
= fopen(hname
, "w")))
750 for (i
= 0; hp
+ i
<= histptr
&& hp
[i
]; i
++)
751 fprintf(fh
, "%s%c", hp
[i
], '\0');
756 # else /* EASY_HISTORY */
759 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
760 * a) permit HISTSIZE to control number of lines of history stored
761 * b) maintain a physical history file
763 * It turns out that there is a lot of ghastly hackery here
768 * save command in history
771 histsave(lno
, cmd
, dowrite
)
779 c
= str_save(cmd
, APERM
);
780 if ((cp
= strchr(c
, '\n')) != NULL
)
783 if (histfd
&& dowrite
)
784 writehistfile(lno
, c
);
788 if (++hp
>= history
+ histsize
) { /* remove oldest command */
789 afree((void*)*history
, APERM
);
790 for (hp
= history
; hp
< history
+ histsize
- 1; hp
++)
798 * Write history data to a file nominated by HISTFILE
799 * if HISTFILE is unset then history still happens, but
800 * the data is not written to a file
801 * All copies of ksh looking at the file will maintain the
802 * same history. This is ksh behaviour.
804 * This stuff uses mmap()
805 * if your system ain't got it - then you'll have to undef HISTORYFILE
809 * Open a history file
811 * Bytes 1, 2: HMAGIC - just to check that we are dealing with
813 * Then follows a number of stored commands
815 * <command byte><command number(4 bytes)><bytes><null>
817 # define HMAGIC1 0xab
818 # define HMAGIC2 0xcd
819 # define COMMAND 0xff
829 if (Flag(FTALKING
) == 0)
836 hname
= str_val(global("HISTFILE"));
839 hname
= str_save(hname
, APERM
);
842 /* we have a file and are interactive */
843 if ((fd
= open(hname
, O_RDWR
|O_CREAT
|O_APPEND
, 0600)) < 0)
846 histfd
= savefd(fd
, 0);
848 (void) flock(histfd
, LOCK_EX
);
850 hsize
= lseek(histfd
, 0L, SEEK_END
);
854 if (sprinkle(histfd
)) {
859 else if (hsize
> 0) {
863 base
= (unsigned char *)mmap(0, hsize
, PROT_READ
, MAP_FLAGS
, histfd
, 0);
865 * check on its validity
867 if (base
== MAP_FAILED
|| *base
!= HMAGIC1
|| base
[1] != HMAGIC2
) {
868 if (base
!= (unsigned char *)-1)
869 munmap((caddr_t
)base
, hsize
);
875 lines
= hist_count_lines(base
+2, hsize
-2);
876 if (lines
> histsize
) {
877 /* we need to make the file smaller */
878 if (hist_shrink(base
, hsize
))
880 munmap((caddr_t
)base
, hsize
);
885 histload(hist_source
, base
+2, hsize
-2);
886 munmap((caddr_t
)base
, hsize
);
888 (void) flock(histfd
, LOCK_UN
);
889 hsize
= lseek(histfd
, 0L, SEEK_END
);
893 shdr
, /* expecting a header */
894 sline
, /* looking for a null byte to end the line */
895 sn1
, /* bytes 1 to 4 of a line no */
900 hist_count_lines(base
, bytes
)
901 register unsigned char *base
;
911 if (*base
== COMMAND
)
921 state
= sline
; break;
924 lines
++, state
= shdr
;
932 * Shrink the history file to histsize lines
935 hist_shrink(oldbase
, oldbytes
)
936 unsigned char *oldbase
;
942 unsigned char *nbase
= oldbase
;
943 int nbytes
= oldbytes
;
945 nbase
= hist_skip_back(nbase
, &nbytes
, histsize
);
948 if (nbase
== oldbase
)
954 (void) shf_snprintf(nfile
, sizeof(nfile
), "%s.%d", hname
, procpid
);
955 if ((fd
= creat(nfile
, 0600)) < 0)
963 if (write(fd
, nbase
, nbytes
) != nbytes
) {
969 * worry about who owns this file
971 if (fstat(histfd
, &statb
) >= 0)
972 fchown(fd
, statb
.st_uid
, statb
.st_gid
);
978 if (rename(nfile
, hname
) < 0)
985 * find a pointer to the data `no' back from the end of the file
986 * return the pointer and the number of bytes left
988 static unsigned char *
989 hist_skip_back(base
, bytes
, no
)
994 register int lines
= 0;
995 register unsigned char *ep
;
997 for (ep
= base
+ *bytes
; --ep
> base
; ) {
998 /* this doesn't really work: the 4 byte line number that is
999 * encoded after the COMMAND byte can itself contain the
1002 for (; ep
> base
&& *ep
!= COMMAND
; ep
--)
1006 if (++lines
== no
) {
1007 *bytes
= *bytes
- ((char *)ep
- (char *)base
);
1015 * load the history structure from the stored data
1018 histload(s
, base
, bytes
)
1020 register unsigned char *base
;
1025 unsigned char *line
;
1027 for (state
= shdr
; bytes
-- > 0; base
++) {
1030 if (*base
== COMMAND
)
1034 lno
= (((*base
)&0xff)<<24);
1038 lno
|= (((*base
)&0xff)<<16);
1042 lno
|= (((*base
)&0xff)<<8);
1046 lno
|= (*base
)&0xff;
1051 if (*base
== '\0') {
1052 /* worry about line numbers */
1053 if (histptr
>= history
&& lno
-1 != s
->line
) {
1054 /* a replacement ? */
1055 histinsert(s
, lno
, line
);
1059 histsave(lno
, (char *)line
, 0);
1068 * Insert a line into the history at a specified number
1071 histinsert(s
, lno
, line
)
1074 unsigned char *line
;
1078 if (lno
>= s
->line
-(histptr
-history
) && lno
<= s
->line
) {
1079 hp
= &histptr
[lno
-s
->line
];
1081 afree((void*)*hp
, APERM
);
1082 *hp
= str_save((char *)line
, APERM
);
1087 * write a command to the end of the history file
1088 * This *MAY* seem easy but it's also necessary to check
1089 * that the history file has not changed in size.
1090 * If it has - then some other shell has written to it
1091 * and we should read those commands to update our history
1094 writehistfile(lno
, cmd
)
1099 unsigned char *base
;
1104 (void) flock(histfd
, LOCK_EX
);
1105 sizenow
= lseek(histfd
, 0L, SEEK_END
);
1106 if (sizenow
!= hsize
) {
1108 * Things have changed
1110 if (sizenow
> hsize
) {
1111 /* someone has added some lines */
1112 bytes
= sizenow
- hsize
;
1113 base
= (unsigned char *)mmap(0, sizenow
, PROT_READ
, MAP_FLAGS
, histfd
, 0);
1114 if (base
== MAP_FAILED
)
1117 if (*new != COMMAND
) {
1118 munmap((caddr_t
)base
, sizenow
);
1121 hist_source
->line
--;
1122 histload(hist_source
, new, bytes
);
1123 hist_source
->line
++;
1124 lno
= hist_source
->line
;
1125 munmap((caddr_t
)base
, sizenow
);
1130 /* we'll give up for now */
1135 * we can write our bit now
1138 hdr
[1] = (lno
>>24)&0xff;
1139 hdr
[2] = (lno
>>16)&0xff;
1140 hdr
[3] = (lno
>>8)&0xff;
1142 (void) write(histfd
, hdr
, 5);
1143 (void) write(histfd
, cmd
, strlen(cmd
)+1);
1144 hsize
= lseek(histfd
, 0L, SEEK_END
);
1145 (void) flock(histfd
, LOCK_UN
);
1154 (void) flock(histfd
, LOCK_UN
);
1155 (void) close(histfd
);
1160 * add magic to the history file
1166 static char mag
[] = { HMAGIC1
, HMAGIC2
};
1168 return(write(fd
, mag
, 2) != 2);
1174 /* No history to be compiled in: dummy routines to avoid lots more ifdefs */
1180 hist_init(Source
UNUSED(*s
))
1188 histsave(int UNUSED(lno
), const char UNUSED(*cmd
), int UNUSED(dowrite
))
1190 errorf("history not enabled");
1192 #endif /* HISTORY */