diff options
Diffstat (limited to 'libbb')
-rw-r--r-- | libbb/lineedit.c | 114 |
1 files changed, 55 insertions, 59 deletions
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index af1b62764..7bcdb954c 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c @@ -954,6 +954,14 @@ static void input_tab(smallint *lastWasTab) #endif /* FEATURE_COMMAND_TAB_COMPLETION */ +line_input_t* FAST_FUNC new_line_input_t(int flags) +{ + line_input_t *n = xzalloc(sizeof(*n)); + n->flags = flags; + return n; +} + + #if MAX_HISTORY > 0 static void save_command_ps_at_cur_history(void) @@ -991,16 +999,23 @@ static int get_next_history(void) #if ENABLE_FEATURE_EDITING_SAVEHISTORY /* We try to ensure that concurrent additions to the history - * do not overwrite each other, and that additions to the history - * by one user are noticed by others. + * do not overwrite each other. * Otherwise shell users get unhappy. * * History file is trimmed lazily, when it grows several times longer * than configured MAX_HISTORY lines. */ +static void free_line_input_t(line_input_t *n) +{ + int i = n->cnt_history; + while (i > 0) + free(n->history[--i]); + free(n); +} + /* state->flags is already checked to be nonzero */ -static void load_history(void) +static void load_history(line_input_t *st_parm) { char *temp_h[MAX_HISTORY]; char *line; @@ -1009,18 +1024,18 @@ static void load_history(void) /* NB: do not trash old history if file can't be opened */ - fp = fopen_for_read(state->hist_file); + fp = fopen_for_read(st_parm->hist_file); if (fp) { /* clean up old history */ - for (idx = state->cnt_history; idx > 0;) { + for (idx = st_parm->cnt_history; idx > 0;) { idx--; - free(state->history[idx]); - state->history[idx] = NULL; + 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)); - state->cnt_history_in_file = idx = 0; + st_parm->cnt_history_in_file = idx = 0; while ((line = xmalloc_fgetline(fp)) != NULL) { if (line[0] == '\0') { free(line); @@ -1028,16 +1043,15 @@ static void load_history(void) } free(temp_h[idx]); temp_h[idx] = line; - state->cnt_history_in_file++; + st_parm->cnt_history_in_file++; idx++; if (idx == MAX_HISTORY) idx = 0; } - state->last_history_end = lseek(fileno(fp), 0, SEEK_CUR); fclose(fp); /* find first non-NULL temp_h[], if any */ - if (state->cnt_history_in_file) { + if (st_parm->cnt_history_in_file) { while (temp_h[idx] == NULL) { idx++; if (idx == MAX_HISTORY) @@ -1045,7 +1059,7 @@ static void load_history(void) } } - /* copy temp_h[] to state->history[] */ + /* copy temp_h[] to st_parm->history[] */ for (i = 0; i < MAX_HISTORY;) { line = temp_h[idx]; if (!line) @@ -1056,71 +1070,60 @@ static void load_history(void) line_len = strlen(line); if (line_len >= MAX_LINELEN) line[MAX_LINELEN-1] = '\0'; - state->history[i++] = line; + st_parm->history[i++] = line; } - state->cnt_history = i; + st_parm->cnt_history = i; } } /* state->flags is already checked to be nonzero */ static void save_history(char *str) { - off_t end; int fd; - int len; + int len, len2; - len = strlen(str); - again: fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666); if (fd < 0) return; - - end = lseek(fd, 0, SEEK_END); - - if (str) { - str[len] = '\n'; - full_write(fd, str, len + 1); - str[len] = '\0'; - str = NULL; - state->cnt_history_in_file++; - } + 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); + str[len] = '\0'; close(fd); - - /* if it was not a 1st write */ - if (state->last_history_end >= 0) { - /* did someone else write anything there? */ - if (state->last_history_end != end) { - load_history(); /* note: updates cnt_history_in_file */ - state->last_history_end = -1; - goto again; - } - } - state->last_history_end = end + len + 1; + if (len2 != len + 1) + return; /* "wtf?" */ /* did we write so much that history file needs trimming? */ + state->cnt_history_in_file++; if (state->cnt_history_in_file > MAX_HISTORY * 4) { FILE *fp; char *new_name; + line_input_t *st_temp; + int i; + + /* we may have concurrently written entries from others. + * load them */ + st_temp = new_line_input_t(state->flags); + st_temp->hist_file = state->hist_file; + load_history(st_temp); - new_name = xasprintf("%s.new", state->hist_file); + /* write out temp file and replace hist_file atomically */ + new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid()); fp = fopen_for_write(new_name); if (fp) { - int i; - - for (i = 0; i < state->cnt_history; i++) { - fprintf(fp, "%s\n", state->history[i]); - } - state->cnt_history_in_file = i; /* == cnt_history */ - state->last_history_end = lseek(fileno(fp), 0, SEEK_CUR); + for (i = 0; i < st_temp->cnt_history; i++) + fprintf(fp, "%s\n", st_temp->history[i]); fclose(fp); - /* replace hist_file atomically */ - rename(new_name, state->hist_file); + if (rename(new_name, state->hist_file) == 0) + state->cnt_history_in_file = st_temp->cnt_history; } free(new_name); + free_line_input_t(st_temp); } } #else -#define load_history() ((void)0) +#define load_history(a) ((void)0) #define save_history(a) ((void)0) #endif /* FEATURE_COMMAND_SAVEHISTORY */ @@ -1490,7 +1493,8 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li state = st ? st : (line_input_t*) &const_int_0; #if ENABLE_FEATURE_EDITING_SAVEHISTORY if ((state->flags & SAVE_HISTORY) && state->hist_file) - load_history(); + if (state->cnt_history == 0) + load_history(state); #endif if (state->flags & DO_HISTORY) state->cur_history = state->cnt_history; @@ -1948,14 +1952,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li return len; /* can't return command_len, DEINIT_S() destroys it */ } -line_input_t* FAST_FUNC new_line_input_t(int flags) -{ - line_input_t *n = xzalloc(sizeof(*n)); - n->flags = flags; - n->last_history_end = -1; - return n; -} - #else #undef read_line_input |