diff options
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/lineedit.c | 114 |
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 | }; |
1217 | line_input_t *new_line_input_t(int flags) FAST_FUNC; | 1216 | line_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 | ||
957 | line_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 | ||
959 | static void save_command_ps_at_cur_history(void) | 967 | static 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 | ||
1009 | static 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 */ |
1003 | static void load_history(void) | 1018 | static 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 */ |
1066 | static void save_history(char *str) | 1080 | static 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 | ||
1951 | line_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 |