1 /* $NetBSD: history.c,v 1.9 2005/06/26 19:09:00 christos 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
19 #include <sys/cdefs.h>
22 __RCSID("$NetBSD: history.c,v 1.9 2005/06/26 19:09:00 christos Exp $");
34 # define HISTFILE "history.ksh"
36 # define HISTFILE ".pdksh_history"
41 /* Defines and includes for the complicated case */
43 # include <sys/file.h>
44 # include <sys/mman.h>
47 * variables for handling the data file
52 static int hist_count_lines
ARGS((unsigned char *, int));
53 static int hist_shrink
ARGS((unsigned char *, int));
54 static unsigned char *hist_skip_back
ARGS((unsigned char *,int *,int));
55 static void histload
ARGS((Source
*, unsigned char *, int));
56 static void histinsert
ARGS((Source
*, int, unsigned char *));
57 static void writehistfile
ARGS((int, char *));
58 static int sprinkle
ARGS((int));
61 # define MAP_FLAGS (MAP_FILE|MAP_PRIVATE)
63 # define MAP_FLAGS MAP_PRIVATE
66 # endif /* of EASY_HISTORY */
68 static int hist_execute
ARGS((char *));
69 static int hist_replace
ARGS((char **, const char *, const char *, int));
70 static char **hist_get
ARGS((const char *, int, int));
71 static char **hist_get_newest
ARGS((int));
72 static char **hist_get_oldest
ARGS((void));
73 static void histbackup
ARGS((void));
75 static char **current
; /* current position in history[] */
76 static int curpos
; /* current index in history[] */
77 static char *hname
; /* current name of history file */
78 static int hstarted
; /* set after hist_init() called */
79 static Source
*hist_source
;
87 struct temp
UNINITIALIZED(*tf
);
88 char *p
, *editor
= (char *) 0;
89 int gflag
= 0, lflag
= 0, nflag
= 0, sflag
= 0, rflag
= 0;
91 char *first
= (char *) 0, *last
= (char *) 0;
92 char **hfirst
, **hlast
, **hp
;
94 if (hist_source
== NULL
) {
95 bi_errorf("not interactive");
99 while ((optc
= ksh_getopt(wp
, &builtin_opt
, "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != EOF
)
102 p
= builtin_opt
.optarg
;
103 if (strcmp(p
, "-") == 0)
106 size_t len
= strlen(p
) + 4;
107 editor
= str_nsave(p
, len
, ATEMP
);
108 strlcat(editor
, " $_", len
);
111 case 'g': /* non-at&t ksh */
123 case 's': /* posix version of -e - */
126 /* kludge city - accept -num as -- -num (kind of) */
127 case '0': case '1': case '2': case '3': case '4':
128 case '5': case '6': case '7': case '8': case '9':
129 p
= shf_smprintf("-%c%s",
130 optc
, builtin_opt
.optarg
);
136 bi_errorf("too many arguments");
143 wp
+= builtin_opt
.optind
;
145 /* Substitute and execute command */
147 char *pat
= (char *) 0, *rep
= (char *) 0;
149 if (editor
|| lflag
|| nflag
|| rflag
) {
150 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
154 /* Check for pattern replacement argument */
155 if (*wp
&& **wp
&& (p
= strchr(*wp
+ 1, '='))) {
156 pat
= str_save(*wp
, ATEMP
);
162 /* Check for search prefix */
163 if (!first
&& (first
= *wp
))
166 bi_errorf("too many arguments");
170 hp
= first
? hist_get(first
, FALSE
, FALSE
)
171 : hist_get_newest(FALSE
);
174 return hist_replace(hp
, pat
, rep
, gflag
);
177 if (editor
&& (lflag
|| nflag
)) {
178 bi_errorf("can't use -l, -n with -e");
182 if (!first
&& (first
= *wp
))
184 if (!last
&& (last
= *wp
))
187 bi_errorf("too many arguments");
191 hfirst
= lflag
? hist_get("-16", TRUE
, TRUE
)
192 : hist_get_newest(FALSE
);
195 /* can't fail if hfirst didn't fail */
196 hlast
= hist_get_newest(FALSE
);
198 /* POSIX says not an error if first/last out of bounds
199 * when range is specified; at&t ksh and pdksh allow out of
200 * bounds for -l as well.
202 hfirst
= hist_get(first
, (lflag
|| last
) ? TRUE
: FALSE
,
203 lflag
? TRUE
: FALSE
);
206 hlast
= last
? hist_get(last
, TRUE
, lflag
? TRUE
: FALSE
)
207 : (lflag
? hist_get_newest(FALSE
) : hfirst
);
211 if (hfirst
> hlast
) {
214 temp
= hfirst
; hfirst
= hlast
; hlast
= temp
;
215 rflag
= !rflag
; /* POSIX */
221 const char *nfmt
= nflag
? "\t" : "%d\t";
223 for (hp
= rflag
? hlast
: hfirst
;
224 hp
>= hfirst
&& hp
<= hlast
; hp
+= rflag
? -1 : 1)
226 shf_fprintf(shl_stdout
, nfmt
,
227 hist_source
->line
- (int) (histptr
- hp
));
228 /* print multi-line commands correctly */
229 for (s
= *hp
; (t
= strchr(s
, '\n')); s
= t
)
230 shf_fprintf(shl_stdout
, "%.*s\t", ++t
- s
, s
);
231 shf_fprintf(shl_stdout
, "%s\n", s
);
233 shf_flush(shl_stdout
);
237 /* Run editor on selected lines, then run resulting commands */
239 tf
= maketemp(ATEMP
, TT_HIST_EDIT
, &e
->temps
);
240 if (!(shf
= tf
->shf
)) {
241 bi_errorf("cannot create temp file %s - %s",
242 tf
->name
, strerror(errno
));
245 for (hp
= rflag
? hlast
: hfirst
;
246 hp
>= hfirst
&& hp
<= hlast
; hp
+= rflag
? -1 : 1)
247 shf_fprintf(shf
, "%s\n", *hp
);
248 if (shf_close(shf
) == EOF
) {
249 bi_errorf("error writing temporary file - %s", strerror(errno
));
253 /* Ignore setstr errors here (arbitrary) */
254 setstr(local("_", FALSE
), tf
->name
, KSH_RETURN_ERROR
);
256 /* XXX: source should not get trashed by this.. */
258 Source
*sold
= source
;
261 ret
= command(editor
? editor
: "${FCEDIT:-/bin/ed} $_");
273 if (!(shf
= shf_open(tf
->name
, O_RDONLY
, 0, 0))) {
274 bi_errorf("cannot open temp file %s", tf
->name
);
278 n
= fstat(shf_fileno(shf
), &statb
) < 0 ? 128
280 Xinit(xs
, xp
, n
, hist_source
->areap
);
281 while ((n
= shf_read(xp
, Xnleft(xs
, xp
), shf
)) > 0) {
283 if (Xnleft(xs
, xp
) <= 0)
284 XcheckN(xs
, xp
, Xlength(xs
, xp
));
287 bi_errorf("error reading temp file %s - %s",
288 tf
->name
, strerror(shf_errno(shf
)));
294 strip_nuls(Xstring(xs
, xp
), Xlength(xs
, xp
));
295 return hist_execute(Xstring(xs
, xp
));
299 /* Save cmd in history, execute cmd (cmd gets trashed) */
310 for (p
= cmd
; p
; p
= q
) {
311 if ((q
= strchr(p
, '\n'))) {
312 *q
++ = '\0'; /* kill the newline */
313 if (!*q
) /* ignore trailing newline */
320 #endif /* EASY_HISTORY */
321 histsave(++(hist_source
->line
), p
, 1);
323 shellf("%s\n", p
); /* POSIX doesn't say this is done... */
324 if ((p
= q
)) /* restore \n (trailing \n not restored) */
328 /* Commands are executed here instead of pushing them onto the
329 * input 'cause posix says the redirection and variable assignments
331 * X=y fc -e - 42 2> /dev/null
332 * are to effect the repeated commands environment.
334 /* XXX: source should not get trashed by this.. */
342 hist_replace(hp
, pat
, rep
, globalv
)
351 line
= str_save(*hp
, ATEMP
);
354 int pat_len
= strlen(pat
);
355 int rep_len
= strlen(rep
);
361 Xinit(xs
, xp
, 128, ATEMP
);
362 for (s
= *hp
; (s1
= strstr(s
, pat
))
363 && (!any_subst
|| globalv
) ; s
= s1
+ pat_len
)
367 XcheckN(xs
, xp
, len
+ rep_len
);
368 memcpy(xp
, s
, len
); /* first part */
370 memcpy(xp
, rep
, rep_len
); /* replacement */
374 bi_errorf("substitution failed");
378 XcheckN(xs
, xp
, len
);
381 line
= Xclose(xs
, xp
);
383 return hist_execute(line
);
387 * get pointer to history given pattern
388 * pattern is a number or string
391 hist_get(str
, approx
, allow_cur
)
396 char **hp
= (char **) 0;
400 hp
= histptr
+ (n
< 0 ? n
: (n
- hist_source
->line
));
403 hp
= hist_get_oldest();
405 bi_errorf("%s: not in history", str
);
408 } else if (hp
> histptr
) {
410 hp
= hist_get_newest(allow_cur
);
412 bi_errorf("%s: not in history", str
);
415 } else if (!allow_cur
&& hp
== histptr
) {
416 bi_errorf("%s: invalid range", str
);
420 int anchored
= *str
== '?' ? (++str
, 0) : 1;
422 /* the -1 is to avoid the current fc command */
423 n
= findhist(histptr
- histlist
- 1, 0, str
, anchored
);
425 bi_errorf("%s: not in history", str
);
433 /* Return a pointer to the newest command in the history */
435 hist_get_newest(allow_cur
)
438 if (histptr
< histlist
|| (!allow_cur
&& histptr
== histlist
)) {
439 bi_errorf("no history (yet)");
447 /* Return a pointer to the newest command in the history */
451 if (histptr
<= histlist
) {
452 bi_errorf("no history (yet)");
458 /******************************/
459 /* Back up over last histsave */
460 /******************************/
464 static int last_line
= -1;
466 if (histptr
>= histlist
&& last_line
!= hist_source
->line
) {
468 afree((void*)*histptr
, APERM
);
470 last_line
= hist_source
->line
;
475 * Return the current position.
493 int last
= histptr
- histlist
;
495 if (n
< 0 || n
>= last
) {
500 current
= &histlist
[n
];
507 * This will become unnecessary if hist_get is modified to allow
508 * searching from positions other than the end, and in either
512 findhist(start
, fwd
, str
, anchored
)
519 int maxhist
= histptr
- histlist
;
520 int incr
= fwd
? 1 : -1;
521 int len
= strlen(str
);
523 if (start
< 0 || start
>= maxhist
)
526 hp
= &histlist
[start
];
527 for (; hp
>= histlist
&& hp
<= histptr
; hp
+= incr
)
528 if ((anchored
&& strncmp(*hp
, str
, len
) == 0)
529 || (!anchored
&& strstr(*hp
, str
)))
530 return hp
- histlist
;
537 * this means reallocating the dataspace
543 if (n
> 0 && n
!= histsize
) {
544 int cursize
= histptr
- histlist
;
546 /* save most recent history */
548 memmove(histlist
, histptr
- n
, n
* sizeof(char *));
552 histlist
= (char **)aresize(histlist
, n
*sizeof(char *), APERM
);
555 histptr
= histlist
+ cursize
;
561 * This can mean reloading/resetting/starting history file
568 /* if not started then nothing to do */
572 /* if the name is the same as the name we have */
573 if (hname
&& strcmp(hname
, name
) == 0)
577 * its a new name - possibly
586 /* yes the file is open */
587 (void) close(histfd
);
592 /* let's reset the history */
593 histptr
= histlist
- 1;
594 hist_source
->line
= 0;
598 hist_init(hist_source
);
602 * initialise the history vector
607 if (histlist
== (char **)NULL
) {
608 histsize
= HISTORYSIZE
;
609 histlist
= (char **)alloc(histsize
*sizeof (char *), APERM
);
610 histptr
= histlist
- 1;
616 * save command in history
619 histsave(lno
, cmd
, dowrite
)
620 int lno
; /* ignored (compatibility with COMPLEX_HISTORY) */
622 int dowrite
; /* ignored (compatibility with COMPLEX_HISTORY) */
624 register char **hp
= histptr
;
627 if (++hp
>= histlist
+ histsize
) { /* remove oldest command */
628 afree((void*)histlist
[0], APERM
);
629 memmove(histlist
, histlist
+ 1,
630 sizeof(histlist
[0]) * (histsize
- 1));
631 hp
= &histlist
[histsize
- 1];
633 *hp
= str_save(cmd
, APERM
);
634 /* trash trailing newline but allow imbedded newlines */
635 cp
= *hp
+ strlen(*hp
);
636 if (cp
> *hp
&& cp
[-1] == '\n')
642 * Append an entry to the last saved command. Used for multiline
646 histappend(cmd
, nl_separate
)
653 hlen
= strlen(*histptr
);
655 if (clen
> 0 && cmd
[clen
-1] == '\n')
657 p
= *histptr
= (char *) aresize(*histptr
, hlen
+ clen
+ 2, APERM
);
661 memcpy(p
, cmd
, clen
);
667 * A simple history file implementation.
668 * At present we only save the history when we exit.
669 * This can cause problems when there are multiple shells are
670 * running under the same user-id. The last shell to exit gets
671 * to save its history.
680 if (Flag(FTALKING
) == 0)
687 if ((f
= str_val(global("HISTFILE"))) == NULL
|| *f
== '\0') {
688 # if 1 /* Don't use history file unless the user asks for it */
692 char *home
= str_val(global("HOME"));
698 hname
= alloc(len
= strlen(home
) + strlen(f
) + 2, APERM
);
699 shf_snprintf(hname
, len
, "%s/%s", home
, f
);
702 hname
= str_save(f
, APERM
);
704 if ((fh
= fopen(hname
, "r"))) {
705 int pos
= 0, nread
= 0;
706 int contin
= 0; /* continuation of previous command */
708 char hline
[LINE
+ 1];
713 nread
= fread(hline
, 1, LINE
, fh
);
718 end
= strchr(hline
+ pos
, 0); /* will always succeed */
720 histappend(hline
+ pos
, 0);
723 histsave(0, hline
+ pos
, 0);
725 pos
= end
- hline
+ 1;
726 contin
= end
== &hline
[nread
];
734 * We check that we do not have more than we are allowed.
735 * If the history file is read-only we do nothing.
736 * Handy for having all shells start with a useful history set.
750 if (hname
== NULL
|| hname
[0] == 0)
753 /* check how many we have */
754 i
= histptr
- histlist
;
756 hp
= &histptr
[-histsize
];
760 fd
= open(hname
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXLOCK
, 0777);
761 /* Remove anything written before we got the lock */
763 if (fd
>= 0 && (fh
= fdopen(fd
, "w"))) {
764 for (i
= 0; hp
+ i
<= histptr
&& hp
[i
]; i
++)
765 fprintf(fh
, "%s%c", hp
[i
], '\0');
770 # else /* EASY_HISTORY */
773 * Routines added by Peter Collinson BSDI(Europe)/Hillside Systems to
774 * a) permit HISTSIZE to control number of lines of history stored
775 * b) maintain a physical history file
777 * It turns out that there is a lot of ghastly hackery here
782 * save command in history
785 histsave(lno
, cmd
, dowrite
)
793 c
= str_save(cmd
, APERM
);
794 if ((cp
= strchr(c
, '\n')) != NULL
)
797 if (histfd
&& dowrite
)
798 writehistfile(lno
, c
);
802 if (++hp
>= histlist
+ histsize
) { /* remove oldest command */
803 afree((void*)*histlist
, APERM
);
804 for (hp
= histlist
; hp
< histlist
+ histsize
- 1; hp
++)
812 * Write history data to a file nominated by HISTFILE
813 * if HISTFILE is unset then history still happens, but
814 * the data is not written to a file
815 * All copies of ksh looking at the file will maintain the
816 * same history. This is ksh behaviour.
818 * This stuff uses mmap()
819 * if your system ain't got it - then you'll have to undef HISTORYFILE
823 * Open a history file
825 * Bytes 1, 2: HMAGIC - just to check that we are dealing with
827 * Then follows a number of stored commands
829 * <command byte><command number(4 bytes)><bytes><null>
831 # define HMAGIC1 0xab
832 # define HMAGIC2 0xcd
833 # define COMMAND 0xff
843 if (Flag(FTALKING
) == 0)
850 hname
= str_val(global("HISTFILE"));
853 hname
= str_save(hname
, APERM
);
856 /* we have a file and are interactive */
857 if ((fd
= open(hname
, O_RDWR
|O_CREAT
|O_APPEND
, 0600)) < 0)
860 histfd
= savefd(fd
, 0);
862 (void) flock(histfd
, LOCK_EX
);
864 hsize
= lseek(histfd
, 0L, SEEK_END
);
868 if (sprinkle(histfd
)) {
873 else if (hsize
> 0) {
877 base
= (unsigned char *)mmap(0, hsize
, PROT_READ
, MAP_FLAGS
, histfd
, 0);
879 * check on its validity
881 if (base
== MAP_FAILED
|| *base
!= HMAGIC1
|| base
[1] != HMAGIC2
) {
882 if (base
!= MAP_FAILED
)
883 munmap((caddr_t
)base
, hsize
);
889 lines
= hist_count_lines(base
+2, hsize
-2);
890 if (lines
> histsize
) {
891 /* we need to make the file smaller */
892 if (hist_shrink(base
, hsize
))
894 munmap((caddr_t
)base
, hsize
);
899 histload(hist_source
, base
+2, hsize
-2);
900 munmap((caddr_t
)base
, hsize
);
902 (void) flock(histfd
, LOCK_UN
);
903 hsize
= lseek(histfd
, 0L, SEEK_END
);
907 shdr
, /* expecting a header */
908 sline
, /* looking for a null byte to end the line */
909 sn1
, /* bytes 1 to 4 of a line no */
914 hist_count_lines(base
, bytes
)
915 register unsigned char *base
;
925 if (*base
== COMMAND
)
935 state
= sline
; break;
938 lines
++, state
= shdr
;
946 * Shrink the history file to histsize lines
949 hist_shrink(oldbase
, oldbytes
)
950 unsigned char *oldbase
;
956 unsigned char *nbase
= oldbase
;
957 int nbytes
= oldbytes
;
959 nbase
= hist_skip_back(nbase
, &nbytes
, histsize
);
962 if (nbase
== oldbase
)
968 (void) shf_snprintf(nfile
, sizeof(nfile
), "%s.%d", hname
, procpid
);
969 if ((fd
= creat(nfile
, 0600)) < 0)
977 if (write(fd
, nbase
, nbytes
) != nbytes
) {
983 * worry about who owns this file
985 if (fstat(histfd
, &statb
) >= 0)
986 fchown(fd
, statb
.st_uid
, statb
.st_gid
);
992 if (rename(nfile
, hname
) < 0)
999 * find a pointer to the data `no' back from the end of the file
1000 * return the pointer and the number of bytes left
1002 static unsigned char *
1003 hist_skip_back(base
, bytes
, no
)
1004 unsigned char *base
;
1008 register int lines
= 0;
1009 register unsigned char *ep
;
1011 for (ep
= base
+ *bytes
; --ep
> base
; ) {
1012 /* this doesn't really work: the 4 byte line number that is
1013 * encoded after the COMMAND byte can itself contain the
1016 for (; ep
> base
&& *ep
!= COMMAND
; ep
--)
1020 if (++lines
== no
) {
1021 *bytes
= *bytes
- ((char *)ep
- (char *)base
);
1029 * load the history structure from the stored data
1032 histload(s
, base
, bytes
)
1034 register unsigned char *base
;
1039 unsigned char *line
= NULL
;
1041 for (state
= shdr
; bytes
-- > 0; base
++) {
1044 if (*base
== COMMAND
)
1048 lno
= (((*base
)&0xff)<<24);
1052 lno
|= (((*base
)&0xff)<<16);
1056 lno
|= (((*base
)&0xff)<<8);
1060 lno
|= (*base
)&0xff;
1065 if (*base
== '\0') {
1066 /* worry about line numbers */
1067 if (histptr
>= histlist
&& lno
-1 != s
->line
) {
1068 /* a replacement ? */
1069 histinsert(s
, lno
, line
);
1073 histsave(lno
, (char *)line
, 0);
1082 * Insert a line into the history at a specified number
1085 histinsert(s
, lno
, line
)
1088 unsigned char *line
;
1092 if (lno
>= s
->line
-(histptr
-histlist
) && lno
<= s
->line
) {
1093 hp
= &histptr
[lno
-s
->line
];
1095 afree((void*)*hp
, APERM
);
1096 *hp
= str_save((char *)line
, APERM
);
1101 * write a command to the end of the history file
1102 * This *MAY* seem easy but it's also necessary to check
1103 * that the history file has not changed in size.
1104 * If it has - then some other shell has written to it
1105 * and we should read those commands to update our history
1108 writehistfile(lno
, cmd
)
1113 unsigned char *base
;
1116 unsigned char hdr
[5];
1118 (void) flock(histfd
, LOCK_EX
);
1119 sizenow
= lseek(histfd
, 0L, SEEK_END
);
1120 if (sizenow
!= hsize
) {
1122 * Things have changed
1124 if (sizenow
> hsize
) {
1125 /* someone has added some lines */
1126 bytes
= sizenow
- hsize
;
1127 base
= (unsigned char *)mmap(0, sizenow
, PROT_READ
, MAP_FLAGS
, histfd
, 0);
1128 if (base
== MAP_FAILED
)
1131 if (*new != COMMAND
) {
1132 munmap((caddr_t
)base
, sizenow
);
1135 hist_source
->line
--;
1136 histload(hist_source
, new, bytes
);
1137 hist_source
->line
++;
1138 lno
= hist_source
->line
;
1139 munmap((caddr_t
)base
, sizenow
);
1144 /* we'll give up for now */
1149 * we can write our bit now
1152 hdr
[1] = (lno
>>24)&0xff;
1153 hdr
[2] = (lno
>>16)&0xff;
1154 hdr
[3] = (lno
>>8)&0xff;
1156 (void) write(histfd
, hdr
, 5);
1157 (void) write(histfd
, cmd
, strlen(cmd
)+1);
1158 hsize
= lseek(histfd
, 0L, SEEK_END
);
1159 (void) flock(histfd
, LOCK_UN
);
1168 (void) flock(histfd
, LOCK_UN
);
1169 (void) close(histfd
);
1174 * add magic to the history file
1180 static unsigned char mag
[] = { HMAGIC1
, HMAGIC2
};
1182 return(write(fd
, mag
, 2) != 2);
1188 /* No history to be compiled in: dummy routines to avoid lots more ifdefs */
1203 histsave(lno
, cmd
, dowrite
)
1208 errorf("history not enabled");
1210 #endif /* HISTORY */