1 /* $OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $ */
11 static void writehistfile(void);
12 static FILE *history_open(void);
13 static int history_load(Source
*);
14 static void history_close(void);
16 static int hist_execute(char *);
17 static int hist_replace(char **, const char *, const char *, int);
18 static char **hist_get(const char *, int, int);
19 static char **hist_get_oldest(void);
20 static void histbackup(void);
23 static char **current
; /* current position in history[] */
24 static char *hname
; /* current name of history file */
25 static int hstarted
; /* set after hist_init() called */
26 static Source
*hist_source
;
27 static uint32_t line_co
;
28 static int real_fs
= 0;
29 static uint32_t ro_fs
= 0;
30 static int lockfd
= -1;
33 static struct stat last_sb
;
39 struct temp
*tf
= NULL
;
40 char *p
, *editor
= NULL
;
41 int gflag
= 0, lflag
= 0, nflag
= 0, sflag
= 0, rflag
= 0;
43 char *first
= NULL
, *last
= NULL
;
44 char **hfirst
, **hlast
, **hp
;
46 if (!Flag(FTALKING_I
)) {
47 bi_errorf("history functions not available");
51 while ((optc
= ksh_getopt(wp
, &builtin_opt
,
52 "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
55 p
= builtin_opt
.optarg
;
56 if (strcmp(p
, "-") == 0)
59 size_t len
= strlen(p
) + 4;
60 editor
= str_nsave(p
, len
, ATEMP
);
61 strlcat(editor
, " $_", len
);
64 case 'g': /* non-at&t ksh */
76 case 's': /* posix version of -e - */
79 /* kludge city - accept -num as -- -num (kind of) */
80 case '0': case '1': case '2': case '3': case '4':
81 case '5': case '6': case '7': case '8': case '9':
82 p
= shf_smprintf("-%c%s",
83 optc
, builtin_opt
.optarg
);
89 bi_errorf("too many arguments");
96 wp
+= builtin_opt
.optind
;
98 /* Substitute and execute command */
100 char *pat
= NULL
, *rep
= NULL
;
102 if (editor
|| lflag
|| nflag
|| rflag
) {
103 bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
107 /* Check for pattern replacement argument */
108 if (*wp
&& **wp
&& (p
= strchr(*wp
+ 1, '='))) {
109 pat
= str_save(*wp
, ATEMP
);
115 /* Check for search prefix */
116 if (!first
&& (first
= *wp
))
119 bi_errorf("too many arguments");
123 hp
= first
? hist_get(first
, false, false) :
124 hist_get_newest(false);
127 return hist_replace(hp
, pat
, rep
, gflag
);
130 if (editor
&& (lflag
|| nflag
)) {
131 bi_errorf("can't use -l, -n with -e");
135 if (!first
&& (first
= *wp
))
137 if (!last
&& (last
= *wp
))
140 bi_errorf("too many arguments");
144 hfirst
= lflag
? hist_get("-16", true, true) :
145 hist_get_newest(false);
148 /* can't fail if hfirst didn't fail */
149 hlast
= hist_get_newest(false);
151 /* POSIX says not an error if first/last out of bounds
152 * when range is specified; at&t ksh and pdksh allow out of
153 * bounds for -l as well.
155 hfirst
= hist_get(first
, (lflag
|| last
) ? true : false,
156 lflag
? true : false);
159 hlast
= last
? hist_get(last
, true, lflag
? true : false) :
160 (lflag
? hist_get_newest(false) : hfirst
);
164 if (hfirst
> hlast
) {
167 temp
= hfirst
; hfirst
= hlast
; hlast
= temp
;
168 rflag
= !rflag
; /* POSIX */
174 const char *nfmt
= nflag
? "\t" : "%d\t";
176 for (hp
= rflag
? hlast
: hfirst
;
177 hp
>= hfirst
&& hp
<= hlast
; hp
+= rflag
? -1 : 1) {
178 shf_fprintf(shl_stdout
, nfmt
,
179 hist_source
->line
- (int) (histptr
- hp
));
180 /* print multi-line commands correctly */
181 for (s
= *hp
; (t
= strchr(s
, '\n')); s
= t
)
182 shf_fprintf(shl_stdout
, "%.*s\t", ++t
- s
, s
);
183 shf_fprintf(shl_stdout
, "%s\n", s
);
185 shf_flush(shl_stdout
);
189 /* Run editor on selected lines, then run resulting commands */
191 tf
= maketemp(ATEMP
, TT_HIST_EDIT
, &e
->temps
);
192 if (!(shf
= tf
->shf
)) {
193 bi_errorf("cannot create temp file %s - %s",
194 tf
->name
, strerror(errno
));
197 for (hp
= rflag
? hlast
: hfirst
;
198 hp
>= hfirst
&& hp
<= hlast
; hp
+= rflag
? -1 : 1)
199 shf_fprintf(shf
, "%s\n", *hp
);
200 if (shf_close(shf
) == EOF
) {
201 bi_errorf("error writing temporary file - %s", strerror(errno
));
205 /* Ignore setstr errors here (arbitrary) */
206 setstr(local("_", false), tf
->name
, KSH_RETURN_ERROR
);
208 /* XXX: source should not get trashed by this.. */
210 Source
*sold
= source
;
213 ret
= command(editor
? editor
: "${FCEDIT:-/bin/ed} $_", 0);
225 if (!(shf
= shf_open(tf
->name
, O_RDONLY
, 0, 0))) {
226 bi_errorf("cannot open temp file %s", tf
->name
);
230 n
= fstat(shf_fileno(shf
), &statb
) < 0 ? 128 :
232 Xinit(xs
, xp
, n
, hist_source
->areap
);
233 while ((n
= shf_read(xp
, Xnleft(xs
, xp
), shf
)) > 0) {
235 if (Xnleft(xs
, xp
) <= 0)
236 XcheckN(xs
, xp
, Xlength(xs
, xp
));
239 bi_errorf("error reading temp file %s - %s",
240 tf
->name
, strerror(shf_errno(shf
)));
246 strip_nuls(Xstring(xs
, xp
), Xlength(xs
, xp
));
247 return hist_execute(Xstring(xs
, xp
));
251 /* Save cmd in history, execute cmd (cmd gets trashed) */
253 hist_execute(char *cmd
)
261 for (p
= cmd
; p
; p
= q
) {
262 if ((q
= strchr(p
, '\n'))) {
263 *q
++ = '\0'; /* kill the newline */
264 if (!*q
) /* ignore trailing newline */
267 histsave(++(hist_source
->line
), p
, 1);
269 shellf("%s\n", p
); /* POSIX doesn't say this is done... */
270 if ((p
= q
)) /* restore \n (trailing \n not restored) */
274 /* Commands are executed here instead of pushing them onto the
275 * input 'cause posix says the redirection and variable assignments
277 * X=y fc -e - 42 2> /dev/null
278 * are to effect the repeated commands environment.
280 /* XXX: source should not get trashed by this.. */
282 ret
= command(cmd
, 0);
288 hist_replace(char **hp
, const char *pat
, const char *rep
, int global
)
293 line
= str_save(*hp
, ATEMP
);
296 int pat_len
= strlen(pat
);
297 int rep_len
= strlen(rep
);
303 Xinit(xs
, xp
, 128, ATEMP
);
304 for (s
= *hp
; (s1
= strstr(s
, pat
)) && (!any_subst
|| global
);
308 XcheckN(xs
, xp
, len
+ rep_len
);
309 memcpy(xp
, s
, len
); /* first part */
311 memcpy(xp
, rep
, rep_len
); /* replacement */
315 bi_errorf("substitution failed");
319 XcheckN(xs
, xp
, len
);
322 line
= Xclose(xs
, xp
);
324 return hist_execute(line
);
328 * get pointer to history given pattern
329 * pattern is a number or string
332 hist_get(const char *str
, int approx
, int allow_cur
)
338 hp
= histptr
+ (n
< 0 ? n
: (n
- hist_source
->line
));
339 if ((long)hp
< (long)history
) {
341 hp
= hist_get_oldest();
343 bi_errorf("%s: not in history", str
);
346 } else if (hp
> histptr
) {
348 hp
= hist_get_newest(allow_cur
);
350 bi_errorf("%s: not in history", str
);
353 } else if (!allow_cur
&& hp
== histptr
) {
354 bi_errorf("%s: invalid range", str
);
358 int anchored
= *str
== '?' ? (++str
, 0) : 1;
360 /* the -1 is to avoid the current fc command */
361 n
= findhist(histptr
- history
- 1, 0, str
, anchored
);
363 bi_errorf("%s: not in history", str
);
371 /* Return a pointer to the newest command in the history */
373 hist_get_newest(int allow_cur
)
375 if (histptr
< history
|| (!allow_cur
&& histptr
== history
)) {
376 bi_errorf("no history (yet)");
384 /* Return a pointer to the oldest command in the history */
386 hist_get_oldest(void)
388 if (histptr
<= history
) {
389 bi_errorf("no history (yet)");
395 /******************************/
396 /* Back up over last histsave */
397 /******************************/
401 static int last_line
= -1;
403 if (histptr
>= history
&& last_line
!= hist_source
->line
) {
405 afree((void*)*histptr
, APERM
);
407 last_line
= hist_source
->line
;
412 * Return the current position.
423 int last
= histptr
- history
;
425 if (n
< 0 || n
>= last
) {
429 current
= &history
[n
];
435 * This will become unnecessary if hist_get is modified to allow
436 * searching from positions other than the end, and in either
440 findhist(int start
, int fwd
, const char *str
, int anchored
)
443 int maxhist
= histptr
- history
;
444 int incr
= fwd
? 1 : -1;
445 int len
= strlen(str
);
447 if (start
< 0 || start
>= maxhist
)
450 hp
= &history
[start
];
451 for (; hp
>= history
&& hp
<= histptr
; hp
+= incr
)
452 if ((anchored
&& strncmp(*hp
, str
, len
) == 0) ||
453 (!anchored
&& strstr(*hp
, str
)))
460 findhistrel(const char *str
)
462 int maxhist
= histptr
- history
;
463 int start
= maxhist
- 1;
475 return start
+ rec
+ 1;
480 * this means reallocating the dataspace
485 if (n
> 0 && n
!= histsize
) {
486 int cursize
= histptr
- history
;
488 /* save most recent history */
490 memmove(history
, histptr
- n
, n
* sizeof(char *));
494 history
= (char **)aresize(history
, n
*sizeof(char *), APERM
);
497 histptr
= history
+ cursize
;
503 * This can mean reloading/resetting/starting history file
507 sethistfile(const char *name
)
509 /* if not started then nothing to do */
513 /* if the name is the same as the name we have */
514 if (hname
&& strcmp(hname
, name
) == 0)
517 * its a new name - possibly
522 /* let's reset the history */
523 histptr
= history
- 1;
524 hist_source
->line
= 0;
530 hist_init(hist_source
);
534 * initialise the history vector
539 if (history
== (char **)NULL
) {
540 histsize
= HISTORYSIZE
;
541 history
= (char **)alloc(histsize
*sizeof (char *), APERM
);
542 histptr
= history
- 1;
552 while (flock(fileno(histfd
), LOCK_EX
) != 0) {
553 if (errno
== EINTR
|| errno
== EAGAIN
)
560 /* should not happen */
561 bi_errorf("recursive lock");
565 for (tries
= 0; ro_fs
== 0 && tries
< 30; tries
++) {
566 if ((lockfd
= open(lname
, O_WRONLY
| O_CREAT
|
567 O_EXLOCK
| O_EXCL
, 0600)) != -1)
569 usleep(100000); /* 100mS */
582 while (flock(fileno(histfd
), LOCK_UN
) != 0) {
583 if (errno
== EINTR
|| errno
== EAGAIN
)
590 /* should not happen */
591 bi_errorf("invalid lock");
594 if (unlink(lname
) == -1) {
595 /* should not happen */
596 bi_errorf("can't unlink lock file");
605 * save command in history
608 histsave(int lno
, const char *cmd
, int dowrite
)
615 if (dowrite
&& histfd
) {
616 if (history_lock() == 0) {
618 if (fstat(fileno(histfd
), &sb
) != -1) {
619 if (timespeccmp(&sb
.st_mtim
,
620 &last_sb
.st_mtim
, ==))
621 ; /* file is unchanged */
624 histptr
= history
- 1;
625 hist_source
->line
= 0;
626 history_load(hist_source
);
632 c
= str_save(cmd
, APERM
);
633 if ((cp
= strchr(c
, '\n')) != NULL
)
637 if (++hp
>= history
+ histsize
) { /* remove oldest command */
638 afree((void*)*history
, APERM
);
639 for (hp
= history
; hp
< history
+ histsize
- 1; hp
++)
645 if (dowrite
&& histfd
) {
648 if (fseeko(histfd
, 0, SEEK_END
) == 0) {
649 fprintf(histfd
, "%s", cmd
);
651 fstat(fileno(histfd
), &last_sb
);
663 int fd
, fddup
, flags
;
668 if (history_lock()) {
669 bi_errorf("initial locking failed, history file won't "
673 flags
= O_RDWR
| O_CREAT
;
675 flags
= O_RDWR
| O_CREAT
| O_EXLOCK
;
677 if ((fd
= open(hname
, flags
, 0600)) == -1)
684 f
= fdopen(fddup
, "r+");
690 /* check for old format */
691 if (fread(old
, sizeof old
, 1, f
) == 1) {
692 if (old
[0] == 0xab && old
[1] == 0xcd) {
693 bi_errorf("binary format history file detected, "
694 "history file won't be used");
700 fstat(fileno(f
), &last_sb
);
721 history_load(Source
*s
)
723 char *p
, line
[LINE
+ 1];
727 /* just read it all; will auto resize history upon next command */
728 for (line_co
= 1; ; line_co
++) {
729 p
= fgets(line
, sizeof line
, histfd
);
730 if (p
== NULL
|| feof(histfd
) || ferror(histfd
))
732 if ((p
= strchr(line
, '\n')) == NULL
) {
733 bi_errorf("history file is corrupt");
739 s
->cmd_offset
= line_co
;
740 histsave(line_co
, (char *)line
, 0);
753 if (Flag(FTALKING
) == 0)
760 hname
= str_val(global("HISTFILE"));
761 if (hname
== NULL
|| strlen(hname
) == 0)
763 hname
= str_save(hname
, APERM
);
764 if (statfs(hname
, &sf
) == 0) {
765 if (!strcmp(sf
.f_fstypename
, "ffs"))
767 ro_fs
= sf
.f_flags
& MNT_RDONLY
;
770 asprintf(&lname
, "%s.lock", hname
);
771 histfd
= history_open();
786 /* see if file has grown over 125% */
787 if (line_co
< histsize
+ (histsize
/ 4))
790 if (ftruncate(fileno(histfd
), 0) == -1)
793 /* rewrite the whole kaboodle */
795 for (i
= 0; i
< histsize
; i
++) {
799 if (fprintf(histfd
, "%s\n", cmd
) == -1)
806 fstat(fileno(histfd
), &last_sb
);
816 /* No history to be compiled in: dummy routines to avoid lots more ifdefs */
830 histsave(int lno
, const char *cmd
, int dowrite
)
832 errorf("history not enabled");