aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libbb.h2
-rw-r--r--libbb/lineedit.c114
2 files changed, 56 insertions, 60 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 7bf9469cb..015374b4a 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1199,7 +1199,6 @@ typedef struct line_input_t {
1199 int cur_history; 1199 int cur_history;
1200#if ENABLE_FEATURE_EDITING_SAVEHISTORY 1200#if ENABLE_FEATURE_EDITING_SAVEHISTORY
1201 unsigned cnt_history_in_file; 1201 unsigned cnt_history_in_file;
1202 off_t last_history_end;
1203 const char *hist_file; 1202 const char *hist_file;
1204#endif 1203#endif
1205 char *history[MAX_HISTORY + 1]; 1204 char *history[MAX_HISTORY + 1];
@@ -1215,6 +1214,7 @@ enum {
1215 FOR_SHELL = DO_HISTORY | SAVE_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION, 1214 FOR_SHELL = DO_HISTORY | SAVE_HISTORY | TAB_COMPLETION | USERNAME_COMPLETION,
1216}; 1215};
1217line_input_t *new_line_input_t(int flags) FAST_FUNC; 1216line_input_t *new_line_input_t(int flags) FAST_FUNC;
1217/* so far static: void free_line_input_t(line_input_t *n) FAST_FUNC; */
1218/* Returns: 1218/* Returns:
1219 * -1 on read errors or EOF, or on bare Ctrl-D, 1219 * -1 on read errors or EOF, or on bare Ctrl-D,
1220 * 0 on ctrl-C (the line entered is still returned in 'command'), 1220 * 0 on ctrl-C (the line entered is still returned in 'command'),
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)
954#endif /* FEATURE_COMMAND_TAB_COMPLETION */ 954#endif /* FEATURE_COMMAND_TAB_COMPLETION */
955 955
956 956
957line_input_t* FAST_FUNC new_line_input_t(int flags)
958{
959 line_input_t *n = xzalloc(sizeof(*n));
960 n->flags = flags;
961 return n;
962}
963
964
957#if MAX_HISTORY > 0 965#if MAX_HISTORY > 0
958 966
959static void save_command_ps_at_cur_history(void) 967static void save_command_ps_at_cur_history(void)
@@ -991,16 +999,23 @@ static int get_next_history(void)
991 999
992#if ENABLE_FEATURE_EDITING_SAVEHISTORY 1000#if ENABLE_FEATURE_EDITING_SAVEHISTORY
993/* We try to ensure that concurrent additions to the history 1001/* We try to ensure that concurrent additions to the history
994 * do not overwrite each other, and that additions to the history 1002 * do not overwrite each other.
995 * by one user are noticed by others.
996 * Otherwise shell users get unhappy. 1003 * Otherwise shell users get unhappy.
997 * 1004 *
998 * History file is trimmed lazily, when it grows several times longer 1005 * History file is trimmed lazily, when it grows several times longer
999 * than configured MAX_HISTORY lines. 1006 * than configured MAX_HISTORY lines.
1000 */ 1007 */
1001 1008
1009static void free_line_input_t(line_input_t *n)
1010{
1011 int i = n->cnt_history;
1012 while (i > 0)
1013 free(n->history[--i]);
1014 free(n);
1015}
1016
1002/* state->flags is already checked to be nonzero */ 1017/* state->flags is already checked to be nonzero */
1003static void load_history(void) 1018static void load_history(line_input_t *st_parm)
1004{ 1019{
1005 char *temp_h[MAX_HISTORY]; 1020 char *temp_h[MAX_HISTORY];
1006 char *line; 1021 char *line;
@@ -1009,18 +1024,18 @@ static void load_history(void)
1009 1024
1010 /* NB: do not trash old history if file can't be opened */ 1025 /* NB: do not trash old history if file can't be opened */
1011 1026
1012 fp = fopen_for_read(state->hist_file); 1027 fp = fopen_for_read(st_parm->hist_file);
1013 if (fp) { 1028 if (fp) {
1014 /* clean up old history */ 1029 /* clean up old history */
1015 for (idx = state->cnt_history; idx > 0;) { 1030 for (idx = st_parm->cnt_history; idx > 0;) {
1016 idx--; 1031 idx--;
1017 free(state->history[idx]); 1032 free(st_parm->history[idx]);
1018 state->history[idx] = NULL; 1033 st_parm->history[idx] = NULL;
1019 } 1034 }
1020 1035
1021 /* fill temp_h[], retaining only last MAX_HISTORY lines */ 1036 /* fill temp_h[], retaining only last MAX_HISTORY lines */
1022 memset(temp_h, 0, sizeof(temp_h)); 1037 memset(temp_h, 0, sizeof(temp_h));
1023 state->cnt_history_in_file = idx = 0; 1038 st_parm->cnt_history_in_file = idx = 0;
1024 while ((line = xmalloc_fgetline(fp)) != NULL) { 1039 while ((line = xmalloc_fgetline(fp)) != NULL) {
1025 if (line[0] == '\0') { 1040 if (line[0] == '\0') {
1026 free(line); 1041 free(line);
@@ -1028,16 +1043,15 @@ static void load_history(void)
1028 } 1043 }
1029 free(temp_h[idx]); 1044 free(temp_h[idx]);
1030 temp_h[idx] = line; 1045 temp_h[idx] = line;
1031 state->cnt_history_in_file++; 1046 st_parm->cnt_history_in_file++;
1032 idx++; 1047 idx++;
1033 if (idx == MAX_HISTORY) 1048 if (idx == MAX_HISTORY)
1034 idx = 0; 1049 idx = 0;
1035 } 1050 }
1036 state->last_history_end = lseek(fileno(fp), 0, SEEK_CUR);
1037 fclose(fp); 1051 fclose(fp);
1038 1052
1039 /* find first non-NULL temp_h[], if any */ 1053 /* find first non-NULL temp_h[], if any */
1040 if (state->cnt_history_in_file) { 1054 if (st_parm->cnt_history_in_file) {
1041 while (temp_h[idx] == NULL) { 1055 while (temp_h[idx] == NULL) {
1042 idx++; 1056 idx++;
1043 if (idx == MAX_HISTORY) 1057 if (idx == MAX_HISTORY)
@@ -1045,7 +1059,7 @@ static void load_history(void)
1045 } 1059 }
1046 } 1060 }
1047 1061
1048 /* copy temp_h[] to state->history[] */ 1062 /* copy temp_h[] to st_parm->history[] */
1049 for (i = 0; i < MAX_HISTORY;) { 1063 for (i = 0; i < MAX_HISTORY;) {
1050 line = temp_h[idx]; 1064 line = temp_h[idx];
1051 if (!line) 1065 if (!line)
@@ -1056,71 +1070,60 @@ static void load_history(void)
1056 line_len = strlen(line); 1070 line_len = strlen(line);
1057 if (line_len >= MAX_LINELEN) 1071 if (line_len >= MAX_LINELEN)
1058 line[MAX_LINELEN-1] = '\0'; 1072 line[MAX_LINELEN-1] = '\0';
1059 state->history[i++] = line; 1073 st_parm->history[i++] = line;
1060 } 1074 }
1061 state->cnt_history = i; 1075 st_parm->cnt_history = i;
1062 } 1076 }
1063} 1077}
1064 1078
1065/* state->flags is already checked to be nonzero */ 1079/* state->flags is already checked to be nonzero */
1066static void save_history(char *str) 1080static void save_history(char *str)
1067{ 1081{
1068 off_t end;
1069 int fd; 1082 int fd;
1070 int len; 1083 int len, len2;
1071 1084
1072 len = strlen(str);
1073 again:
1074 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666); 1085 fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0666);
1075 if (fd < 0) 1086 if (fd < 0)
1076 return; 1087 return;
1077 1088 xlseek(fd, 0, SEEK_END); /* paranoia */
1078 end = lseek(fd, 0, SEEK_END); 1089 len = strlen(str);
1079 1090 str[len] = '\n'; /* we (try to) do atomic write */
1080 if (str) { 1091 len2 = full_write(fd, str, len + 1);
1081 str[len] = '\n'; 1092 str[len] = '\0';
1082 full_write(fd, str, len + 1);
1083 str[len] = '\0';
1084 str = NULL;
1085 state->cnt_history_in_file++;
1086 }
1087 close(fd); 1093 close(fd);
1088 1094 if (len2 != len + 1)
1089 /* if it was not a 1st write */ 1095 return; /* "wtf?" */
1090 if (state->last_history_end >= 0) {
1091 /* did someone else write anything there? */
1092 if (state->last_history_end != end) {
1093 load_history(); /* note: updates cnt_history_in_file */
1094 state->last_history_end = -1;
1095 goto again;
1096 }
1097 }
1098 state->last_history_end = end + len + 1;
1099 1096
1100 /* did we write so much that history file needs trimming? */ 1097 /* did we write so much that history file needs trimming? */
1098 state->cnt_history_in_file++;
1101 if (state->cnt_history_in_file > MAX_HISTORY * 4) { 1099 if (state->cnt_history_in_file > MAX_HISTORY * 4) {
1102 FILE *fp; 1100 FILE *fp;
1103 char *new_name; 1101 char *new_name;
1102 line_input_t *st_temp;
1103 int i;
1104
1105 /* we may have concurrently written entries from others.
1106 * load them */
1107 st_temp = new_line_input_t(state->flags);
1108 st_temp->hist_file = state->hist_file;
1109 load_history(st_temp);
1104 1110
1105 new_name = xasprintf("%s.new", state->hist_file); 1111 /* write out temp file and replace hist_file atomically */
1112 new_name = xasprintf("%s.%u.new", state->hist_file, (int) getpid());
1106 fp = fopen_for_write(new_name); 1113 fp = fopen_for_write(new_name);
1107 if (fp) { 1114 if (fp) {
1108 int i; 1115 for (i = 0; i < st_temp->cnt_history; i++)
1109 1116 fprintf(fp, "%s\n", st_temp->history[i]);
1110 for (i = 0; i < state->cnt_history; i++) {
1111 fprintf(fp, "%s\n", state->history[i]);
1112 }
1113 state->cnt_history_in_file = i; /* == cnt_history */
1114 state->last_history_end = lseek(fileno(fp), 0, SEEK_CUR);
1115 fclose(fp); 1117 fclose(fp);
1116 /* replace hist_file atomically */ 1118 if (rename(new_name, state->hist_file) == 0)
1117 rename(new_name, state->hist_file); 1119 state->cnt_history_in_file = st_temp->cnt_history;
1118 } 1120 }
1119 free(new_name); 1121 free(new_name);
1122 free_line_input_t(st_temp);
1120 } 1123 }
1121} 1124}
1122#else 1125#else
1123#define load_history() ((void)0) 1126#define load_history(a) ((void)0)
1124#define save_history(a) ((void)0) 1127#define save_history(a) ((void)0)
1125#endif /* FEATURE_COMMAND_SAVEHISTORY */ 1128#endif /* FEATURE_COMMAND_SAVEHISTORY */
1126 1129
@@ -1490,7 +1493,8 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1490 state = st ? st : (line_input_t*) &const_int_0; 1493 state = st ? st : (line_input_t*) &const_int_0;
1491#if ENABLE_FEATURE_EDITING_SAVEHISTORY 1494#if ENABLE_FEATURE_EDITING_SAVEHISTORY
1492 if ((state->flags & SAVE_HISTORY) && state->hist_file) 1495 if ((state->flags & SAVE_HISTORY) && state->hist_file)
1493 load_history(); 1496 if (state->cnt_history == 0)
1497 load_history(state);
1494#endif 1498#endif
1495 if (state->flags & DO_HISTORY) 1499 if (state->flags & DO_HISTORY)
1496 state->cur_history = state->cnt_history; 1500 state->cur_history = state->cnt_history;
@@ -1948,14 +1952,6 @@ int FAST_FUNC read_line_input(const char *prompt, char *command, int maxsize, li
1948 return len; /* can't return command_len, DEINIT_S() destroys it */ 1952 return len; /* can't return command_len, DEINIT_S() destroys it */
1949} 1953}
1950 1954
1951line_input_t* FAST_FUNC new_line_input_t(int flags)
1952{
1953 line_input_t *n = xzalloc(sizeof(*n));
1954 n->flags = flags;
1955 n->last_history_end = -1;
1956 return n;
1957}
1958
1959#else 1955#else
1960 1956
1961#undef read_line_input 1957#undef read_line_input