From bede215cf105377a1127532d2d710924cb58cc39 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 4 Sep 2011 16:12:33 +0200 Subject: [PATCH] lineedit: add support for history saving on exit Based on the patch by Dennis Groenen Signed-off-by: Denys Vlasenko --- include/libbb.h | 9 +++++++ libbb/Config.src | 7 +++++ libbb/lineedit.c | 68 +++++++++++++++++++++++++++++++++++++++++++----- shell/ash.c | 4 +++ shell/hush.c | 4 +++ 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/include/libbb.h b/include/libbb.h index 63d041957..91343a95e 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1421,6 +1421,12 @@ typedef struct line_input_t { int cur_history; int max_history; /* must never be <= 0 */ # if ENABLE_FEATURE_EDITING_SAVEHISTORY + /* meaning of this field depends on FEATURE_EDITING_SAVE_ON_EXIT: + * if !FEATURE_EDITING_SAVE_ON_EXIT: "how many lines are + * in on-disk history" + * if FEATURE_EDITING_SAVE_ON_EXIT: "how many in-memory lines are + * also in on-disk history (and thus need to be skipped on save)" + */ unsigned cnt_history_in_file; const char *hist_file; # endif @@ -1446,6 +1452,9 @@ line_input_t *new_line_input_t(int flags) FAST_FUNC; * >0 length of input string, including terminating '\n' */ int read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) FAST_FUNC; +# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT +void save_history(line_input_t *st); +# endif #else #define MAX_HISTORY 0 int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC; diff --git a/libbb/Config.src b/libbb/Config.src index aa442365a..f6f88b9ce 100644 --- a/libbb/Config.src +++ b/libbb/Config.src @@ -94,6 +94,13 @@ config FEATURE_EDITING_SAVEHISTORY help Enable history saving in shells. +config FEATURE_EDITING_SAVE_ON_EXIT + bool "Save history on shell exit, not after every command" + default n + depends on FEATURE_EDITING_SAVEHISTORY + help + Save history on shell exit, not after every command. + config FEATURE_REVERSE_SEARCH bool "Reverse history search" default y diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 5d139043a..0786f9ae6 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -1351,7 +1351,9 @@ static void load_history(line_input_t *st_parm) /* fill temp_h[], retaining only last MAX_HISTORY lines */ memset(temp_h, 0, sizeof(temp_h)); - st_parm->cnt_history_in_file = idx = 0; + idx = 0; + if (!ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) + st_parm->cnt_history_in_file = 0; while ((line = xmalloc_fgetline(fp)) != NULL) { if (line[0] == '\0') { free(line); @@ -1359,7 +1361,8 @@ static void load_history(line_input_t *st_parm) } free(temp_h[idx]); temp_h[idx] = line; - st_parm->cnt_history_in_file++; + if (!ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) + st_parm->cnt_history_in_file++; idx++; if (idx == st_parm->max_history) idx = 0; @@ -1389,15 +1392,66 @@ static void load_history(line_input_t *st_parm) st_parm->history[i++] = line; } st_parm->cnt_history = i; + if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) + st_parm->cnt_history_in_file = i; } } -/* state->flags is already checked to be nonzero */ +# if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT +void save_history(line_input_t *st) +{ + FILE *fp; + + if (!(st->flags & SAVE_HISTORY)) + return; + if (!st->hist_file) + return; + if (st->cnt_history <= st->cnt_history_in_file) + return; + + fp = fopen(st->hist_file, "a"); + if (fp) { + int i, fd; + char *new_name; + line_input_t *st_temp; + + for (i = st->cnt_history_in_file; i < st->cnt_history; i++) + fprintf(fp, "%s\n", st->history[i]); + fclose(fp); + + /* we may have concurrently written entries from others. + * load them */ + st_temp = new_line_input_t(st->flags); + st_temp->hist_file = st->hist_file; + st_temp->max_history = st->max_history; + load_history(st_temp); + + /* write out temp file and replace hist_file atomically */ + new_name = xasprintf("%s.%u.new", st->hist_file, (int) getpid()); + fd = open(new_name, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd >= 0) { + fp = xfdopen_for_write(fd); + for (i = 0; i < st_temp->cnt_history; i++) + fprintf(fp, "%s\n", st_temp->history[i]); + fclose(fp); + if (rename(new_name, st->hist_file) == 0) + st->cnt_history_in_file = st_temp->cnt_history; + } + free(new_name); + free_line_input_t(st_temp); + } +} +# else static void save_history(char *str) { int fd; int len, len2; + if (!(state->flags & SAVE_HISTORY)) + return; + if (!state->hist_file) + return; + fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); if (fd < 0) return; @@ -1441,6 +1495,7 @@ static void save_history(char *str) free_line_input_t(st_temp); } } +# endif # else # define load_history(a) ((void)0) # define save_history(a) ((void)0) @@ -1469,15 +1524,16 @@ static void remember_in_history(char *str) for (i = 0; i < state->max_history-1; i++) state->history[i] = state->history[i+1]; /* i == state->max_history-1 */ + if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT && state->cnt_history_in_file) + state->cnt_history_in_file--; } /* i <= state->max_history-1 */ state->history[i++] = xstrdup(str); /* i <= state->max_history */ state->cur_history = i; state->cnt_history = i; -# if ENABLE_FEATURE_EDITING_SAVEHISTORY - if ((state->flags & SAVE_HISTORY) && state->hist_file) - save_history(str); +# if ENABLE_FEATURE_EDITING_SAVEHISTORY && !ENABLE_FEATURE_EDITING_SAVE_ON_EXIT + save_history(str); # endif IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;) } diff --git a/shell/ash.c b/shell/ash.c index bf376bd0d..14472cb61 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -12888,6 +12888,10 @@ exitshell(void) char *p; int status; +#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT + save_history(line_input_state); +#endif + status = exitstatus; TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); if (setjmp(loc.loc)) { diff --git a/shell/hush.c b/shell/hush.c index 42143fd9e..a9e2dd311 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -1541,6 +1541,10 @@ static sighandler_t pick_sighandler(unsigned sig) static void hush_exit(int exitcode) NORETURN; static void hush_exit(int exitcode) { +#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT + save_history(G.line_input_state); +#endif + fflush_all(); if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { char *argv[3];