aboutsummaryrefslogtreecommitdiff
path: root/libbb/lineedit.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-03-23 06:33:37 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-03-23 06:33:37 +0000
commitc0ea82a457410b46265cddc4883fbf9fcfdb2e24 (patch)
tree1e313bdded3760ddc00f8b585a215c2ef981ebbb /libbb/lineedit.c
parentddbee974b4263a7089f18bdd02d79cfa708a4a22 (diff)
downloadbusybox-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.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)
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