diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-23 06:33:37 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-03-23 06:33:37 +0000 |
commit | c0ea82a457410b46265cddc4883fbf9fcfdb2e24 (patch) | |
tree | 1e313bdded3760ddc00f8b585a215c2ef981ebbb /libbb/lineedit.c | |
parent | ddbee974b4263a7089f18bdd02d79cfa708a4a22 (diff) | |
download | busybox-w32-c0ea82a457410b46265cddc4883fbf9fcfdb2e24.tar.gz busybox-w32-c0ea82a457410b46265cddc4883fbf9fcfdb2e24.tar.bz2 busybox-w32-c0ea82a457410b46265cddc4883fbf9fcfdb2e24.zip |
libbb: revent previous version of "concurrent history updating"
and replace it with one which does not "snoop" history written
by others. (1) it is what bug 185 needs, and (2) it is less bloaty:
function old new delta
load_history - 252 +252
read_line_input 3155 3287 +132
next_token 914 918 +4
qrealloc 36 33 -3
getoptscmd 713 708 -5
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/2 up/down: 388/-8) Total: 380 bytes
Diffstat (limited to 'libbb/lineedit.c')
-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) | |||
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 |