shell: update HISTFILESIZE code to be actually useful
No binary (build failed)
—
(log yok)
| @@ -1989,7 +1989,7 @@ typedef struct line_input_t { | |
| # if MAX_HISTORY | |
| int cnt_history; | |
| int cur_history; | |
| - | int max_history; /* must never be <= 0 */ |
| + | 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 | |
| @@ -182,8 +182,8 @@ config FEATURE_EDITING_VI | |
| config FEATURE_EDITING_HISTORY | |
| int "History size" | |
| # Don't allow way too big values here, code uses fixed "char *history[N]" struct member | |
| - | range 0 9999 |
| - | default 255 |
| + | range 0 2000 |
| + | default 200 |
| depends on FEATURE_EDITING | |
| help | |
| Specify command history size (0 - disable). | |
| @@ -1406,8 +1406,8 @@ unsigned FAST_FUNC size_from_HISTFILESIZE(const char *hp) | |
| int size = MAX_HISTORY; | |
| if (hp) { | |
| size = atoi(hp); | |
| - | if (size <= 0) |
| - | return 1; |
| + | if (size < 0) |
| + | return 0; |
| if (size > MAX_HISTORY) | |
| return MAX_HISTORY; | |
| } | |
| @@ -1501,18 +1501,21 @@ static void load_history(line_input_t *st_parm) | |
| /* NB: do not trash old history if file can't be opened */ | |
| fp = fopen_for_read(st_parm->hist_file); | |
| - | if (fp) { |
| - | /* clean up old history */ |
| - | for (idx = st_parm->cnt_history; idx > 0;) { |
| - | idx--; |
| - | free(st_parm->history[idx]); |
| - | st_parm->history[idx] = NULL; |
| - | } |
| + | if (!fp) |
| + | return; |
| - | /* fill temp_h[], retaining only last MAX_HISTORY lines */ |
| - | memset(temp_h, 0, sizeof(temp_h)); |
| - | idx = 0; |
| - | st_parm->cnt_history_in_file = 0; |
| + | /* clean up old history */ |
| + | for (idx = st_parm->cnt_history; idx > 0;) { |
| + | idx--; |
| + | free(st_parm->history[idx]); |
| + | st_parm->history[idx] = NULL; |
| + | } |
| + | |
| + | /* fill temp_h[], retaining only last max_history lines */ |
| + | memset(temp_h, 0, sizeof(temp_h)); |
| + | idx = 0; |
| + | st_parm->cnt_history_in_file = 0; |
| + | if (st_parm->max_history != 0) { |
| while ((line = xmalloc_fgetline(fp)) != NULL) { | |
| if (line[0] == '\0') { | |
| free(line); | |
| @@ -1525,34 +1528,34 @@ static void load_history(line_input_t *st_parm) | |
| if (idx == st_parm->max_history) | |
| idx = 0; | |
| } | |
| - | fclose(fp); |
| - | |
| - | /* find first non-NULL temp_h[], if any */ |
| - | if (st_parm->cnt_history_in_file) { |
| - | while (temp_h[idx] == NULL) { |
| - | idx++; |
| - | if (idx == st_parm->max_history) |
| - | idx = 0; |
| - | } |
| - | } |
| + | } |
| + | fclose(fp); |
| - | /* copy temp_h[] to st_parm->history[] */ |
| - | for (i = 0; i < st_parm->max_history;) { |
| - | line = temp_h[idx]; |
| - | if (!line) |
| - | break; |
| + | /* find first non-NULL temp_h[], if any */ |
| + | if (st_parm->cnt_history_in_file != 0) { |
| + | while (temp_h[idx] == NULL) { |
| idx++; | |
| if (idx == st_parm->max_history) | |
| idx = 0; | |
| - | line_len = strlen(line); |
| - | if (line_len >= MAX_LINELEN) |
| - | line[MAX_LINELEN-1] = '\0'; |
| - | st_parm->history[i++] = line; |
| } | |
| - | st_parm->cnt_history = i; |
| - | if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) |
| - | st_parm->cnt_history_in_file = i; |
| } | |
| + | |
| + | /* copy temp_h[] to st_parm->history[] */ |
| + | for (i = 0; i < st_parm->max_history;) { |
| + | line = temp_h[idx]; |
| + | if (!line) |
| + | break; |
| + | idx++; |
| + | if (idx == st_parm->max_history) |
| + | idx = 0; |
| + | line_len = strlen(line); |
| + | if (line_len >= MAX_LINELEN) |
| + | line[MAX_LINELEN-1] = '\0'; |
| + | st_parm->history[i++] = line; |
| + | } |
| + | st_parm->cnt_history = i; |
| + | if (ENABLE_FEATURE_EDITING_SAVE_ON_EXIT) |
| + | st_parm->cnt_history_in_file = i; |
| } | |
| # if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | |
| @@ -1563,14 +1566,23 @@ void FAST_FUNC save_history(line_input_t *st) | |
| if (!st || !st->hist_file) | |
| return; | |
| if (st->cnt_history <= st->cnt_history_in_file) | |
| - | return; |
| + | return; /* no new entries were added */ |
| + | /* note: if st->max_history is 0, we do not abort: we truncate the history to 0 lines */ |
| - | fp = fopen(st->hist_file, "a"); |
| + | fp = fopen(st->hist_file, (st->max_history == 0 ? "w" : "a")); |
| if (fp) { | |
| int i, fd; | |
| char *new_name; | |
| line_input_t *st_temp; | |
| + | /* max_history==0 needs special-casing in general code, |
| + | * just handle it in a simpler way: */ |
| + | if (st->max_history == 0) { |
| + | /* fopen("w") already truncated it */ |
| + | fclose(fp); |
| + | return; |
| + | } |
| + | |
| for (i = st->cnt_history_in_file; i < st->cnt_history; i++) | |
| fprintf(fp, "%s\n", st->history[i]); | |
| fclose(fp); | |
| @@ -1580,6 +1592,8 @@ void FAST_FUNC save_history(line_input_t *st) | |
| st_temp = new_line_input_t(st->flags); | |
| st_temp->hist_file = st->hist_file; | |
| st_temp->max_history = st->max_history; | |
| + | /* load no more than max_history last lines */ |
| + | /* (in unlikely case that file disappeared, st_temp gets empty history) */ |
| load_history(st_temp); | |
| /* write out temp file and replace hist_file atomically */ | |
| @@ -1609,7 +1623,6 @@ static void save_history(char *str) | |
| fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600); | |
| if (fd < 0) | |
| return; | |
| - | xlseek(fd, 0, SEEK_END); /* paranoia */ |
| len = strlen(str); | |
| str[len] = '\n'; /* we (try to) do atomic write */ | |
| len2 = full_write(fd, str, len + 1); | |
| @@ -1664,13 +1677,10 @@ static void remember_in_history(char *str) | |
| if (str[0] == '\0') | |
| return; | |
| i = state->cnt_history; | |
| - | /* Don't save dupes */ |
| - | if (i && strcmp(state->history[i-1], str) == 0) |
| + | /* Don't save dups */ |
| + | if (i != 0 && strcmp(state->history[i-1], str) == 0) |
| return; | |
| - | free(state->history[state->max_history]); /* redundant, paranoia */ |
| - | state->history[state->max_history] = NULL; /* redundant, paranoia */ |
| - | |
| /* If history[] is full, remove the oldest command */ | |
| /* we need to keep history[state->max_history] empty, hence >=, not > */ | |
| if (i >= state->max_history) { | |
| @@ -1683,7 +1693,7 @@ static void remember_in_history(char *str) | |
| state->cnt_history_in_file--; | |
| # endif | |
| } | |
| - | /* i <= state->max_history-1 */ |
| + | /* i < state->max_history */ |
| state->history[i++] = xstrdup(str); | |
| /* i <= state->max_history */ | |
| state->cur_history = i; | |
| @@ -166,9 +166,10 @@ config FEATURE_SH_HISTFILESIZE | |
| default y | |
| depends on SHELL_ASH || SHELL_HUSH | |
| help | |
| - | This option makes busybox shells to use $HISTFILESIZE variable |
| - | to set shell history size. Note that its max value is capped |
| - | by "History size" setting in library tuning section. |
| + | This option makes busybox shells to use $HISTSIZE and |
| + | $HISTFILESIZE variables to set shell history size. |
| + | Note that its max value is capped by "History size" setting |
| + | in library tuning section. |
| config FEATURE_SH_EMBEDDED_SCRIPTS | |
| bool "Embed scripts in the binary" | |
| @@ -14525,8 +14525,25 @@ exitshell(void) | |
| char *p; | |
| #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | |
| - | save_history(line_input_state); /* may be NULL */ |
| + | if (line_input_state) { |
| + | const char *hp; |
| + | # if ENABLE_FEATURE_SH_HISTFILESIZE |
| + | // in bash: |
| + | // HISTFILESIZE controls the on-disk history file size (in lines, 0=no history): |
| + | // "When this variable is assigned a value, the history file is truncated, if necessary" |
| + | // but we do it only at exit, not on assignment: |
| + | /* Use HISTFILESIZE to limit file size */ |
| + | hp = lookupvar("HISTFILESIZE"); |
| + | if (hp) |
| + | line_input_state->max_history = size_from_HISTFILESIZE(hp); |
| + | # endif |
| + | /* HISTFILE: "If unset, the command history is not saved when a shell exits." */ |
| + | hp = lookupvar("HISTFILE"); |
| + | line_input_state->hist_file = hp; |
| + | save_history(line_input_state); /* no-op if hist_file is NULL */ |
| + | } |
| #endif | |
| + | |
| savestatus = exitstatus; | |
| TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); | |
| if (setjmp(loc.loc)) | |
| @@ -14867,7 +14884,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |
| if (hp) | |
| line_input_state->hist_file = xstrdup(hp); | |
| # if ENABLE_FEATURE_SH_HISTFILESIZE | |
| - | hp = lookupvar("HISTFILESIZE"); |
| + | hp = lookupvar("HISTSIZE"); |
| + | /* Using HISTFILESIZE above to limit max_history would be WRONG: |
| + | * users may set HISTFILESIZE=0 in their profile scripts |
| + | * to prevent _saving_ of history files, but still want to have |
| + | * non-zero history limit for in-memory list. |
| + | */ |
| line_input_state->max_history = size_from_HISTFILESIZE(hp); | |
| # endif | |
| } | |
| @@ -2099,11 +2099,29 @@ static sighandler_t pick_sighandler(unsigned sig) | |
| return handler; | |
| } | |
| + | static const char* FAST_FUNC get_local_var_value(const char *name); |
| + | |
| /* Restores tty foreground process group, and exits. */ | |
| static void hush_exit(int exitcode) | |
| { | |
| #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | |
| - | save_history(G.line_input_state); /* may be NULL */ |
| + | if (G.line_input_state) { |
| + | const char *hp; |
| + | # if ENABLE_FEATURE_SH_HISTFILESIZE |
| + | // in bash: |
| + | // HISTFILESIZE controls the on-disk history file size (in lines, 0=no history): |
| + | // "When this variable is assigned a value, the history file is truncated, if necessary" |
| + | // but we do it only at exit, not on every assignment: |
| + | /* Use HISTFILESIZE to limit file size */ |
| + | hp = get_local_var_value("HISTFILESIZE"); |
| + | if (hp) |
| + | G.line_input_state->max_history = size_from_HISTFILESIZE(hp); |
| + | # endif |
| + | /* HISTFILE: "If unset, the command history is not saved when a shell exits." */ |
| + | hp = get_local_var_value("HISTFILE"); |
| + | G.line_input_state->hist_file = hp; |
| + | save_history(G.line_input_state); /* no-op if hist_file is NULL */ |
| + | } |
| #endif | |
| fflush_all(); | |
| @@ -10427,7 +10445,7 @@ int hush_main(int argc, char **argv) | |
| if (!get_local_var_value("PATH")) | |
| set_local_var_from_halves("PATH", bb_default_root_path); | |
| - | /* PS1/PS2 are set later, if we determine that we are interactive */ |
| + | /* PS1/PS2/HISTFILE are set later, if we determine that we are interactive */ |
| /* bash also exports SHLVL and _, | |
| * and sets (but doesn't export) the following variables: | |
| @@ -10449,7 +10467,6 @@ int hush_main(int argc, char **argv) | |
| * BASH_SOURCE=() | |
| * DIRSTACK=() | |
| * PIPESTATUS=([0]="0") | |
| - | * HISTFILE=/<xxx>/.bash_history |
| * HISTFILESIZE=500 | |
| * HISTSIZE=500 | |
| * MAILCHECK=60 | |
| @@ -10809,18 +10826,30 @@ int hush_main(int argc, char **argv) | |
| const char *hp = get_local_var_value("HISTFILE"); | |
| if (!hp) { | |
| hp = get_local_var_value("HOME"); | |
| - | if (hp) |
| + | if (hp) { |
| hp = concat_path_file(hp, ".hush_history"); | |
| + | /* Make HISTFILE set on exit (else history won't be saved) */ |
| + | set_local_var_from_halves("HISTFILE", hp); |
| + | } |
| } else { | |
| hp = xstrdup(hp); | |
| } | |
| if (hp) { | |
| G.line_input_state->hist_file = hp; | |
| - | //set_local_var(xasprintf("HISTFILE=%s", ...)); |
| } | |
| # if ENABLE_FEATURE_SH_HISTFILESIZE | |
| - | hp = get_local_var_value("HISTFILESIZE"); |
| + | hp = get_local_var_value("HISTSIZE"); |
| + | /* Using HISTFILESIZE above to limit max_history would be WRONG: |
| + | * users may set HISTFILESIZE=0 in their profile scripts |
| + | * to prevent _saving_ of history files, but still want to have |
| + | * non-zero history limit for in-memory list. |
| + | */ |
| + | // in bash, runtime history size is controlled by HISTSIZE (0=no history), |
| + | // HISTFILESIZE controls on-disk history file size (in lines, 0=no history): |
| G.line_input_state->max_history = size_from_HISTFILESIZE(hp); | |
| + | // HISTFILESIZE: "The shell sets the default value to the value of HISTSIZE after reading any startup files." |
| + | // HISTSIZE: "The shell sets the default value to 500 after reading any startup files." |
| + | // (meaning: if the value wasn't set after startup files, the default value is set as described above) |
| # endif | |
| } | |
| # endif | |