aboutsummaryrefslogtreecommitdiff
path: root/libbb
diff options
context:
space:
mode:
Diffstat (limited to 'libbb')
-rw-r--r--libbb/lineedit.c114
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