diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-01-22 07:21:38 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-01-22 07:21:38 +0000 |
commit | 8e1c71529c2bf38a04d4a117e625e59044a0785a (patch) | |
tree | 2f115293c25e7ee9307f268ec198e2cf486ff070 /shell/cmdedit.c | |
parent | 00cdbd8fc20a4e2e2208f90a2691a3806c931b06 (diff) | |
download | busybox-w32-8e1c71529c2bf38a04d4a117e625e59044a0785a.tar.gz busybox-w32-8e1c71529c2bf38a04d4a117e625e59044a0785a.tar.bz2 busybox-w32-8e1c71529c2bf38a04d4a117e625e59044a0785a.zip |
Convert cmdedit into more generic line input facility
(make history and completion optional at runtime).
Use it for fdisk, as an example.
Some unrelated fixes in fdisk are also here.
Diffstat (limited to 'shell/cmdedit.c')
-rw-r--r-- | shell/cmdedit.c | 316 |
1 files changed, 157 insertions, 159 deletions
diff --git a/shell/cmdedit.c b/shell/cmdedit.c index a1432af15..554a4ebec 100644 --- a/shell/cmdedit.c +++ b/shell/cmdedit.c | |||
@@ -30,7 +30,6 @@ | |||
30 | 30 | ||
31 | #include <sys/ioctl.h> | 31 | #include <sys/ioctl.h> |
32 | #include "busybox.h" | 32 | #include "busybox.h" |
33 | #include "cmdedit.h" | ||
34 | 33 | ||
35 | 34 | ||
36 | /* FIXME: obsolete CONFIG item? */ | 35 | /* FIXME: obsolete CONFIG item? */ |
@@ -51,7 +50,6 @@ | |||
51 | /* Entire file (except TESTing part) sits inside this #if */ | 50 | /* Entire file (except TESTing part) sits inside this #if */ |
52 | #if ENABLE_FEATURE_COMMAND_EDITING | 51 | #if ENABLE_FEATURE_COMMAND_EDITING |
53 | 52 | ||
54 | |||
55 | #if ENABLE_LOCALE_SUPPORT | 53 | #if ENABLE_LOCALE_SUPPORT |
56 | #define Isprint(c) isprint(c) | 54 | #define Isprint(c) isprint(c) |
57 | #else | 55 | #else |
@@ -61,29 +59,21 @@ | |||
61 | #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ | 59 | #define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \ |
62 | (ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION || ENABLE_FEATURE_SH_FANCY_PROMPT) | 60 | (ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION || ENABLE_FEATURE_SH_FANCY_PROMPT) |
63 | 61 | ||
64 | /* Maximum length of command line history */ | ||
65 | #if !ENABLE_FEATURE_COMMAND_HISTORY | ||
66 | #define MAX_HISTORY 15 | ||
67 | #else | ||
68 | #define MAX_HISTORY (CONFIG_FEATURE_COMMAND_HISTORY + 0) | ||
69 | #endif | ||
70 | 62 | ||
63 | static line_input_t *state; | ||
71 | 64 | ||
72 | /* Current termios and the previous termios before starting sh */ | ||
73 | static struct termios initial_settings, new_settings; | 65 | static struct termios initial_settings, new_settings; |
74 | 66 | ||
75 | static | 67 | static volatile unsigned cmdedit_termw = 80; /* actual terminal width */ |
76 | volatile unsigned cmdedit_termw = 80; /* actual terminal width */ | ||
77 | |||
78 | 68 | ||
79 | static int cmdedit_x; /* real x terminal position */ | 69 | static int cmdedit_x; /* real x terminal position */ |
80 | static int cmdedit_y; /* pseudoreal y terminal position */ | 70 | static int cmdedit_y; /* pseudoreal y terminal position */ |
81 | static int cmdedit_prmt_len; /* length of prompt (without colors etc) */ | 71 | static int cmdedit_prmt_len; /* length of prompt (without colors etc) */ |
82 | 72 | ||
83 | static int cursor; | 73 | static unsigned cursor; |
84 | static int len; | 74 | static unsigned command_len; |
85 | static char *command_ps; | 75 | static char *command_ps; |
86 | static SKIP_FEATURE_SH_FANCY_PROMPT(const) char *cmdedit_prompt; | 76 | static const char *cmdedit_prompt; |
87 | 77 | ||
88 | #if ENABLE_FEATURE_SH_FANCY_PROMPT | 78 | #if ENABLE_FEATURE_SH_FANCY_PROMPT |
89 | static char *hostname_buf; | 79 | static char *hostname_buf; |
@@ -142,7 +132,7 @@ static void cmdedit_set_out_char(int next_char) | |||
142 | /* Move to end of line (by printing all chars till the end) */ | 132 | /* Move to end of line (by printing all chars till the end) */ |
143 | static void input_end(void) | 133 | static void input_end(void) |
144 | { | 134 | { |
145 | while (cursor < len) | 135 | while (cursor < command_len) |
146 | cmdedit_set_out_char(' '); | 136 | cmdedit_set_out_char(' '); |
147 | } | 137 | } |
148 | 138 | ||
@@ -200,7 +190,7 @@ static void input_backward(unsigned num) | |||
200 | static void put_prompt(void) | 190 | static void put_prompt(void) |
201 | { | 191 | { |
202 | out1str(cmdedit_prompt); | 192 | out1str(cmdedit_prompt); |
203 | cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */ | 193 | cmdedit_x = cmdedit_prmt_len; |
204 | cursor = 0; | 194 | cursor = 0; |
205 | // Huh? what if cmdedit_prmt_len >= width? | 195 | // Huh? what if cmdedit_prmt_len >= width? |
206 | cmdedit_y = 0; /* new quasireal y */ | 196 | cmdedit_y = 0; /* new quasireal y */ |
@@ -231,7 +221,7 @@ static void input_delete(int save) | |||
231 | { | 221 | { |
232 | int j = cursor; | 222 | int j = cursor; |
233 | 223 | ||
234 | if (j == len) | 224 | if (j == command_len) |
235 | return; | 225 | return; |
236 | 226 | ||
237 | #if ENABLE_FEATURE_COMMAND_EDITING_VI | 227 | #if ENABLE_FEATURE_COMMAND_EDITING_VI |
@@ -249,7 +239,7 @@ static void input_delete(int save) | |||
249 | #endif | 239 | #endif |
250 | 240 | ||
251 | strcpy(command_ps + j, command_ps + j + 1); | 241 | strcpy(command_ps + j, command_ps + j + 1); |
252 | len--; | 242 | command_len--; |
253 | input_end(); /* rewrite new line */ | 243 | input_end(); /* rewrite new line */ |
254 | cmdedit_set_out_char(' '); /* erase char */ | 244 | cmdedit_set_out_char(' '); /* erase char */ |
255 | input_backward(cursor - j); /* back to old pos cursor */ | 245 | input_backward(cursor - j); /* back to old pos cursor */ |
@@ -285,7 +275,7 @@ static void input_backspace(void) | |||
285 | /* Move forward one character */ | 275 | /* Move forward one character */ |
286 | static void input_forward(void) | 276 | static void input_forward(void) |
287 | { | 277 | { |
288 | if (cursor < len) | 278 | if (cursor < command_len) |
289 | cmdedit_set_out_char(command_ps[cursor + 1]); | 279 | cmdedit_set_out_char(command_ps[cursor + 1]); |
290 | } | 280 | } |
291 | 281 | ||
@@ -372,54 +362,50 @@ enum { | |||
372 | FIND_FILE_ONLY = 2, | 362 | FIND_FILE_ONLY = 2, |
373 | }; | 363 | }; |
374 | 364 | ||
375 | #if ENABLE_ASH | ||
376 | const char *cmdedit_path_lookup; | ||
377 | #endif | ||
378 | static int path_parse(char ***p, int flags) | 365 | static int path_parse(char ***p, int flags) |
379 | { | 366 | { |
380 | int npth; | 367 | int npth; |
381 | const char *tmp; | 368 | const char *tmp; |
382 | #if ENABLE_ASH | 369 | const char *pth; |
383 | const char *pth = cmdedit_path_lookup; | 370 | char **res; |
384 | #else | ||
385 | const char *pth = getenv("PATH") | ||
386 | #endif | ||
387 | 371 | ||
388 | /* if not setenv PATH variable, to search cur dir "." */ | 372 | /* if not setenv PATH variable, to search cur dir "." */ |
389 | if (flags != FIND_EXE_ONLY) | 373 | if (flags != FIND_EXE_ONLY) |
390 | return 1; | 374 | return 1; |
375 | |||
376 | if (state->flags & WITH_PATH_LOOKUP) | ||
377 | pth = state->path_lookup; | ||
378 | else | ||
379 | pth = getenv("PATH"); | ||
391 | /* PATH=<empty> or PATH=:<empty> */ | 380 | /* PATH=<empty> or PATH=:<empty> */ |
392 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) | 381 | if (!pth || !pth[0] || LONE_CHAR(pth, ':')) |
393 | return 1; | 382 | return 1; |
394 | 383 | ||
395 | tmp = pth; | 384 | tmp = pth; |
396 | npth = 0; | 385 | npth = 1; /* path component count */ |
397 | |||
398 | while (1) { | 386 | while (1) { |
399 | npth++; /* count words is + 1 count ':' */ | ||
400 | tmp = strchr(tmp, ':'); | 387 | tmp = strchr(tmp, ':'); |
401 | if (!tmp) | 388 | if (!tmp) |
402 | break; | 389 | break; |
403 | if (*++tmp == '\0') | 390 | if (*++tmp == '\0') |
404 | break; /* :<empty> */ | 391 | break; /* :<empty> */ |
392 | npth++; | ||
405 | } | 393 | } |
406 | 394 | ||
407 | *p = xmalloc(npth * sizeof(char *)); | 395 | res = xmalloc(npth * sizeof(char*)); |
408 | 396 | res[0] = xstrdup(pth); | |
409 | tmp = pth; | 397 | tmp = pth; |
410 | (*p)[0] = xstrdup(tmp); | 398 | npth = 1; |
411 | npth = 1; /* count words is + 1 count ':' */ | ||
412 | |||
413 | while (1) { | 399 | while (1) { |
414 | tmp = strchr(tmp, ':'); | 400 | tmp = strchr(tmp, ':'); |
415 | if (!tmp) | 401 | if (!tmp) |
416 | break; | 402 | break; |
417 | (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */ | 403 | *tmp++ = '\0'; /* ':' -> '\0' */ |
418 | if (*++tmp == 0) | 404 | if (*tmp == '\0') |
419 | break; /* :<empty> */ | 405 | break; /* :<empty> */ |
420 | (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */ | 406 | res[npth++] = tmp; |
421 | } | 407 | } |
422 | 408 | *p = res; | |
423 | return npth; | 409 | return npth; |
424 | } | 410 | } |
425 | 411 | ||
@@ -742,6 +728,9 @@ static int match_compare(const void *a, const void *b) | |||
742 | /* Do TAB completion */ | 728 | /* Do TAB completion */ |
743 | static void input_tab(int *lastWasTab) | 729 | static void input_tab(int *lastWasTab) |
744 | { | 730 | { |
731 | if (!(state->flags & TAB_COMPLETION)) | ||
732 | return; | ||
733 | |||
745 | if (!*lastWasTab) { | 734 | if (!*lastWasTab) { |
746 | char *tmp, *tmp1; | 735 | char *tmp, *tmp1; |
747 | int len_found; | 736 | int len_found; |
@@ -764,13 +753,13 @@ static void input_tab(int *lastWasTab) | |||
764 | #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION | 753 | #if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION |
765 | /* If the word starts with `~' and there is no slash in the word, | 754 | /* If the word starts with `~' and there is no slash in the word, |
766 | * then try completing this word as a username. */ | 755 | * then try completing this word as a username. */ |
767 | 756 | if (state->flags & USERNAME_COMPLETION) | |
768 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) | 757 | if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0) |
769 | username_tab_completion(matchBuf, NULL); | 758 | username_tab_completion(matchBuf, NULL); |
770 | if (!matches) | ||
771 | #endif | 759 | #endif |
772 | /* Try to match any executable in our path and everything | 760 | /* Try to match any executable in our path and everything |
773 | * in the current working directory */ | 761 | * in the current working directory */ |
762 | if (!matches) | ||
774 | exe_n_cwd_tab_completion(matchBuf, find_type); | 763 | exe_n_cwd_tab_completion(matchBuf, find_type); |
775 | /* Sort, then remove any duplicates found */ | 764 | /* Sort, then remove any duplicates found */ |
776 | if (matches) { | 765 | if (matches) { |
@@ -855,51 +844,48 @@ static void input_tab(int *lastWasTab) | |||
855 | } | 844 | } |
856 | } | 845 | } |
857 | 846 | ||
847 | #else | ||
848 | #define input_tab(a) ((void)0) | ||
858 | #endif /* FEATURE_COMMAND_TAB_COMPLETION */ | 849 | #endif /* FEATURE_COMMAND_TAB_COMPLETION */ |
859 | 850 | ||
860 | 851 | ||
861 | #if MAX_HISTORY > 0 | 852 | #if MAX_HISTORY > 0 |
862 | 853 | ||
863 | static char *history[MAX_HISTORY+1]; /* history + current */ | 854 | /* state->flags is already checked to be nonzero */ |
864 | /* saved history lines */ | ||
865 | static int n_history; | ||
866 | /* current pointer to history line */ | ||
867 | static int cur_history; | ||
868 | |||
869 | static void get_previous_history(void) | 855 | static void get_previous_history(void) |
870 | { | 856 | { |
871 | if (command_ps[0] != '\0' || history[cur_history] == NULL) { | 857 | if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) { |
872 | free(history[cur_history]); | 858 | free(state->history[state->cur_history]); |
873 | history[cur_history] = xstrdup(command_ps); | 859 | state->history[state->cur_history] = xstrdup(command_ps); |
874 | } | 860 | } |
875 | cur_history--; | 861 | state->cur_history--; |
876 | } | 862 | } |
877 | 863 | ||
878 | static int get_next_history(void) | 864 | static int get_next_history(void) |
879 | { | 865 | { |
880 | int ch = cur_history; | 866 | if (state->flags & DO_HISTORY) { |
881 | 867 | int ch = state->cur_history; | |
882 | if (ch < n_history) { | 868 | if (ch < state->cnt_history) { |
883 | get_previous_history(); /* save the current history line */ | 869 | get_previous_history(); /* save the current history line */ |
884 | cur_history = ch + 1; | 870 | state->cur_history = ch + 1; |
885 | return cur_history; | 871 | return state->cur_history; |
886 | } else { | 872 | } |
887 | beep(); | ||
888 | return 0; | ||
889 | } | 873 | } |
874 | beep(); | ||
875 | return 0; | ||
890 | } | 876 | } |
891 | 877 | ||
892 | #if ENABLE_FEATURE_COMMAND_SAVEHISTORY | 878 | #if ENABLE_FEATURE_COMMAND_SAVEHISTORY |
879 | /* state->flags is already checked to be nonzero */ | ||
893 | void load_history(const char *fromfile) | 880 | void load_history(const char *fromfile) |
894 | { | 881 | { |
895 | FILE *fp; | 882 | FILE *fp; |
896 | int hi; | 883 | int hi; |
897 | 884 | ||
898 | /* cleanup old */ | 885 | /* cleanup old */ |
899 | 886 | for (hi = state->cnt_history; hi > 0;) { | |
900 | for (hi = n_history; hi > 0;) { | ||
901 | hi--; | 887 | hi--; |
902 | free(history[hi]); | 888 | free(state->history[hi]); |
903 | } | 889 | } |
904 | 890 | ||
905 | fp = fopen(fromfile, "r"); | 891 | fp = fopen(fromfile, "r"); |
@@ -917,29 +903,62 @@ void load_history(const char *fromfile) | |||
917 | free(hl); | 903 | free(hl); |
918 | continue; | 904 | continue; |
919 | } | 905 | } |
920 | history[hi++] = hl; | 906 | state->history[hi++] = hl; |
921 | } | 907 | } |
922 | fclose(fp); | 908 | fclose(fp); |
923 | } | 909 | } |
924 | cur_history = n_history = hi; | 910 | state->cur_history = state->cnt_history = hi; |
925 | } | 911 | } |
926 | 912 | ||
913 | /* state->flags is already checked to be nonzero */ | ||
927 | void save_history(const char *tofile) | 914 | void save_history(const char *tofile) |
928 | { | 915 | { |
929 | FILE *fp = fopen(tofile, "w"); | 916 | FILE *fp; |
930 | 917 | ||
918 | fp = fopen(tofile, "w"); | ||
931 | if (fp) { | 919 | if (fp) { |
932 | int i; | 920 | int i; |
933 | 921 | ||
934 | for (i = 0; i < n_history; i++) { | 922 | for (i = 0; i < state->cnt_history; i++) { |
935 | fprintf(fp, "%s\n", history[i]); | 923 | fprintf(fp, "%s\n", state->history[i]); |
936 | } | 924 | } |
937 | fclose(fp); | 925 | fclose(fp); |
938 | } | 926 | } |
939 | } | 927 | } |
928 | #else | ||
929 | #define load_history(a) ((void)0) | ||
930 | #define save_history(a) ((void)0) | ||
940 | #endif /* FEATURE_COMMAND_SAVEHISTORY */ | 931 | #endif /* FEATURE_COMMAND_SAVEHISTORY */ |
941 | 932 | ||
942 | #endif /* MAX_HISTORY > 0 */ | 933 | static void remember_in_history(const char *str) |
934 | { | ||
935 | int i; | ||
936 | |||
937 | if (!(state->flags & DO_HISTORY)) | ||
938 | return; | ||
939 | |||
940 | i = state->cnt_history; | ||
941 | free(state->history[MAX_HISTORY]); | ||
942 | state->history[MAX_HISTORY] = NULL; | ||
943 | /* After max history, remove the oldest command */ | ||
944 | if (i >= MAX_HISTORY) { | ||
945 | free(state->history[0]); | ||
946 | for (i = 0; i < MAX_HISTORY-1; i++) | ||
947 | state->history[i] = state->history[i+1]; | ||
948 | } | ||
949 | // Maybe "if (!i || strcmp(history[i-1], command) != 0) ..." | ||
950 | // (i.e. do not save dups?) | ||
951 | state->history[i++] = xstrdup(str); | ||
952 | state->cur_history = i; | ||
953 | state->cnt_history = i; | ||
954 | if (state->flags & SAVE_HISTORY) | ||
955 | save_history(state->hist_file); | ||
956 | USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;) | ||
957 | } | ||
958 | |||
959 | #else /* MAX_HISTORY == 0 */ | ||
960 | #define remember_in_history(a) ((void)0) | ||
961 | #endif /* MAX_HISTORY */ | ||
943 | 962 | ||
944 | 963 | ||
945 | /* | 964 | /* |
@@ -960,13 +979,6 @@ void save_history(const char *tofile) | |||
960 | */ | 979 | */ |
961 | 980 | ||
962 | #if ENABLE_FEATURE_COMMAND_EDITING_VI | 981 | #if ENABLE_FEATURE_COMMAND_EDITING_VI |
963 | static int vi_mode; | ||
964 | |||
965 | void setvimode(int viflag) | ||
966 | { | ||
967 | vi_mode = viflag; | ||
968 | } | ||
969 | |||
970 | static void | 982 | static void |
971 | vi_Word_motion(char *command, int eat) | 983 | vi_Word_motion(char *command, int eat) |
972 | { | 984 | { |
@@ -1058,13 +1070,11 @@ vi_back_motion(char *command) | |||
1058 | input_backward(1); | 1070 | input_backward(1); |
1059 | } | 1071 | } |
1060 | } | 1072 | } |
1061 | #else | ||
1062 | enum { vi_mode = 0 }; | ||
1063 | #endif | 1073 | #endif |
1064 | 1074 | ||
1065 | 1075 | ||
1066 | /* | 1076 | /* |
1067 | * cmdedit_read_input and its helpers | 1077 | * read_line_input and its helpers |
1068 | */ | 1078 | */ |
1069 | 1079 | ||
1070 | #if !ENABLE_FEATURE_SH_FANCY_PROMPT | 1080 | #if !ENABLE_FEATURE_SH_FANCY_PROMPT |
@@ -1190,7 +1200,7 @@ static void parse_prompt(const char *prmt_ptr) | |||
1190 | cmdedit_prmt_len += cur_prmt_len; | 1200 | cmdedit_prmt_len += cur_prmt_len; |
1191 | prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); | 1201 | prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf); |
1192 | } | 1202 | } |
1193 | if (pwd_buf!=(char *)bb_msg_unknown) | 1203 | if (pwd_buf != (char *)bb_msg_unknown) |
1194 | free(pwd_buf); | 1204 | free(pwd_buf); |
1195 | cmdedit_prompt = prmt_mem_ptr; | 1205 | cmdedit_prompt = prmt_mem_ptr; |
1196 | put_prompt(); | 1206 | put_prompt(); |
@@ -1217,7 +1227,7 @@ static void cmdedit_setwidth(unsigned w, int redraw_flg) | |||
1217 | /* new y for current cursor */ | 1227 | /* new y for current cursor */ |
1218 | int new_y = (cursor + cmdedit_prmt_len) / w; | 1228 | int new_y = (cursor + cmdedit_prmt_len) / w; |
1219 | /* redraw */ | 1229 | /* redraw */ |
1220 | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor); | 1230 | redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor); |
1221 | fflush(stdout); | 1231 | fflush(stdout); |
1222 | } | 1232 | } |
1223 | } | 1233 | } |
@@ -1275,9 +1285,10 @@ static void cmdedit_init(void) | |||
1275 | #undef CTRL | 1285 | #undef CTRL |
1276 | #define CTRL(a) ((a) & ~0x40) | 1286 | #define CTRL(a) ((a) & ~0x40) |
1277 | 1287 | ||
1278 | 1288 | int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st) | |
1279 | int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | ||
1280 | { | 1289 | { |
1290 | static const int null_flags; | ||
1291 | |||
1281 | int lastWasTab = FALSE; | 1292 | int lastWasTab = FALSE; |
1282 | unsigned int ic; | 1293 | unsigned int ic; |
1283 | unsigned char c; | 1294 | unsigned char c; |
@@ -1286,18 +1297,28 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1286 | smallint vi_cmdmode = 0; | 1297 | smallint vi_cmdmode = 0; |
1287 | smalluint prevc; | 1298 | smalluint prevc; |
1288 | #endif | 1299 | #endif |
1300 | |||
1301 | // FIXME: audit & improve this | ||
1302 | if (maxsize > BUFSIZ) | ||
1303 | maxsize = BUFSIZ; | ||
1304 | |||
1305 | /* With null flags, no other fields are ever used */ | ||
1306 | state = st ? st : (line_input_t*) &null_flags; | ||
1307 | if (state->flags & SAVE_HISTORY) | ||
1308 | load_history(state->hist_file); | ||
1309 | |||
1289 | /* prepare before init handlers */ | 1310 | /* prepare before init handlers */ |
1290 | cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ | 1311 | cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */ |
1291 | len = 0; | 1312 | command_len = 0; |
1292 | command_ps = command; | 1313 | command_ps = command; |
1293 | command[0] = '\0'; | 1314 | command[0] = '\0'; |
1294 | 1315 | ||
1295 | getTermSettings(0, (void *) &initial_settings); | 1316 | getTermSettings(0, (void *) &initial_settings); |
1296 | memcpy(&new_settings, &initial_settings, sizeof(struct termios)); | 1317 | memcpy(&new_settings, &initial_settings, sizeof(new_settings)); |
1297 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ | 1318 | new_settings.c_lflag &= ~ICANON; /* unbuffered input */ |
1298 | /* Turn off echoing and CTRL-C, so we can trap it */ | 1319 | /* Turn off echoing and CTRL-C, so we can trap it */ |
1299 | new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG); | 1320 | new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG); |
1300 | /* Hmm, in linux c_cc[] not parsed if set ~ICANON */ | 1321 | /* Hmm, in linux c_cc[] is not parsed if ICANON is off */ |
1301 | new_settings.c_cc[VMIN] = 1; | 1322 | new_settings.c_cc[VMIN] = 1; |
1302 | new_settings.c_cc[VTIME] = 0; | 1323 | new_settings.c_cc[VTIME] = 0; |
1303 | /* Turn off CTRL-C, so we can trap it */ | 1324 | /* Turn off CTRL-C, so we can trap it */ |
@@ -1354,34 +1375,18 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1354 | vi_case(CTRL('C')|vbit:) | 1375 | vi_case(CTRL('C')|vbit:) |
1355 | /* Control-c -- stop gathering input */ | 1376 | /* Control-c -- stop gathering input */ |
1356 | goto_new_line(); | 1377 | goto_new_line(); |
1357 | #if !ENABLE_ASH | 1378 | command_len = 0; |
1358 | command[0] = '\0'; | 1379 | break_out = -1; /* "do not append '\n'" */ |
1359 | len = 0; | ||
1360 | lastWasTab = FALSE; | ||
1361 | put_prompt(); | ||
1362 | #else | ||
1363 | len = 0; | ||
1364 | break_out = -1; /* to control traps */ | ||
1365 | #endif | ||
1366 | break; | 1380 | break; |
1367 | case CTRL('D'): | 1381 | case CTRL('D'): |
1368 | /* Control-d -- Delete one character, or exit | 1382 | /* Control-d -- Delete one character, or exit |
1369 | * if the len=0 and no chars to delete */ | 1383 | * if the len=0 and no chars to delete */ |
1370 | if (len == 0) { | 1384 | if (command_len == 0) { |
1371 | errno = 0; | 1385 | errno = 0; |
1372 | prepare_to_die: | 1386 | prepare_to_die: |
1373 | // So, our API depends on whether we have ash compiled in or not? Crap... | ||
1374 | #if !ENABLE_ASH | ||
1375 | printf("exit"); | ||
1376 | goto_new_line(); | ||
1377 | /* cmdedit_reset_term() called in atexit */ | ||
1378 | // FIXME. this is definitely not good | ||
1379 | exit(EXIT_SUCCESS); | ||
1380 | #else | ||
1381 | /* to control stopped jobs */ | 1387 | /* to control stopped jobs */ |
1382 | break_out = len = -1; | 1388 | break_out = command_len = -1; |
1383 | break; | 1389 | break; |
1384 | #endif | ||
1385 | } | 1390 | } |
1386 | input_delete(0); | 1391 | input_delete(0); |
1387 | break; | 1392 | break; |
@@ -1407,23 +1412,21 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1407 | break; | 1412 | break; |
1408 | 1413 | ||
1409 | case '\t': | 1414 | case '\t': |
1410 | #if ENABLE_FEATURE_COMMAND_TAB_COMPLETION | ||
1411 | input_tab(&lastWasTab); | 1415 | input_tab(&lastWasTab); |
1412 | #endif | ||
1413 | break; | 1416 | break; |
1414 | 1417 | ||
1415 | #if ENABLE_FEATURE_EDITING_FANCY_KEYS | 1418 | #if ENABLE_FEATURE_EDITING_FANCY_KEYS |
1416 | case CTRL('K'): | 1419 | case CTRL('K'): |
1417 | /* Control-k -- clear to end of line */ | 1420 | /* Control-k -- clear to end of line */ |
1418 | command[cursor] = 0; | 1421 | command[cursor] = 0; |
1419 | len = cursor; | 1422 | command_len = cursor; |
1420 | printf("\033[J"); | 1423 | printf("\033[J"); |
1421 | break; | 1424 | break; |
1422 | case CTRL('L'): | 1425 | case CTRL('L'): |
1423 | vi_case(CTRL('L')|vbit:) | 1426 | vi_case(CTRL('L')|vbit:) |
1424 | /* Control-l -- clear screen */ | 1427 | /* Control-l -- clear screen */ |
1425 | printf("\033[H"); | 1428 | printf("\033[H"); |
1426 | redraw(0, len - cursor); | 1429 | redraw(0, command_len - cursor); |
1427 | break; | 1430 | break; |
1428 | #endif | 1431 | #endif |
1429 | 1432 | ||
@@ -1439,12 +1442,11 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1439 | vi_case(CTRL('P')|vbit:) | 1442 | vi_case(CTRL('P')|vbit:) |
1440 | vi_case('k'|vbit:) | 1443 | vi_case('k'|vbit:) |
1441 | /* Control-p -- Get previous command from history */ | 1444 | /* Control-p -- Get previous command from history */ |
1442 | if (cur_history > 0) { | 1445 | if ((state->flags & DO_HISTORY) && state->cur_history > 0) { |
1443 | get_previous_history(); | 1446 | get_previous_history(); |
1444 | goto rewrite_line; | 1447 | goto rewrite_line; |
1445 | } else { | ||
1446 | beep(); | ||
1447 | } | 1448 | } |
1449 | beep(); | ||
1448 | break; | 1450 | break; |
1449 | #endif | 1451 | #endif |
1450 | 1452 | ||
@@ -1454,7 +1456,8 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1454 | /* Control-U -- Clear line before cursor */ | 1456 | /* Control-U -- Clear line before cursor */ |
1455 | if (cursor) { | 1457 | if (cursor) { |
1456 | strcpy(command, command + cursor); | 1458 | strcpy(command, command + cursor); |
1457 | redraw(cmdedit_y, len -= cursor); | 1459 | command_len -= cursor; |
1460 | redraw(cmdedit_y, command_len); | ||
1458 | } | 1461 | } |
1459 | break; | 1462 | break; |
1460 | #endif | 1463 | #endif |
@@ -1571,7 +1574,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1571 | break; | 1574 | break; |
1572 | case '$': /* "d$", "c$" */ | 1575 | case '$': /* "d$", "c$" */ |
1573 | clear_to_eol: | 1576 | clear_to_eol: |
1574 | while (cursor < len) | 1577 | while (cursor < command_len) |
1575 | input_delete(1); | 1578 | input_delete(1); |
1576 | break; | 1579 | break; |
1577 | } | 1580 | } |
@@ -1599,7 +1602,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1599 | case '\x1b': /* ESC */ | 1602 | case '\x1b': /* ESC */ |
1600 | 1603 | ||
1601 | #if ENABLE_FEATURE_COMMAND_EDITING_VI | 1604 | #if ENABLE_FEATURE_COMMAND_EDITING_VI |
1602 | if (vi_mode) { | 1605 | if (state->flags & VI_MODE) { |
1603 | /* ESC: insert mode --> command mode */ | 1606 | /* ESC: insert mode --> command mode */ |
1604 | vi_cmdmode = 1; | 1607 | vi_cmdmode = 1; |
1605 | input_backward(1); | 1608 | input_backward(1); |
@@ -1634,7 +1637,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1634 | #if MAX_HISTORY > 0 | 1637 | #if MAX_HISTORY > 0 |
1635 | case 'A': | 1638 | case 'A': |
1636 | /* Up Arrow -- Get previous command from history */ | 1639 | /* Up Arrow -- Get previous command from history */ |
1637 | if (cur_history > 0) { | 1640 | if ((state->flags & DO_HISTORY) && state->cur_history > 0) { |
1638 | get_previous_history(); | 1641 | get_previous_history(); |
1639 | goto rewrite_line; | 1642 | goto rewrite_line; |
1640 | } | 1643 | } |
@@ -1647,9 +1650,9 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1647 | rewrite_line: | 1650 | rewrite_line: |
1648 | /* Rewrite the line with the selected history item */ | 1651 | /* Rewrite the line with the selected history item */ |
1649 | /* change command */ | 1652 | /* change command */ |
1650 | len = strlen(strcpy(command, history[cur_history])); | 1653 | command_len = strlen(strcpy(command, state->history[state->cur_history])); |
1651 | /* redraw and go to eol (bol, in vi */ | 1654 | /* redraw and go to eol (bol, in vi */ |
1652 | redraw(cmdedit_y, vi_mode ? 9999 : 0); | 1655 | redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0); |
1653 | break; | 1656 | break; |
1654 | #endif | 1657 | #endif |
1655 | case 'C': | 1658 | case 'C': |
@@ -1700,18 +1703,18 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1700 | if (!Isprint(c)) /* Skip non-printable characters */ | 1703 | if (!Isprint(c)) /* Skip non-printable characters */ |
1701 | break; | 1704 | break; |
1702 | 1705 | ||
1703 | if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */ | 1706 | if (command_len >= (maxsize - 2)) /* Need to leave space for enter */ |
1704 | break; | 1707 | break; |
1705 | 1708 | ||
1706 | len++; | 1709 | command_len++; |
1707 | if (cursor == (len - 1)) { /* Append if at the end of the line */ | 1710 | if (cursor == (command_len - 1)) { /* Append if at the end of the line */ |
1708 | command[cursor] = c; | 1711 | command[cursor] = c; |
1709 | command[cursor+1] = '\0'; | 1712 | command[cursor+1] = '\0'; |
1710 | cmdedit_set_out_char(' '); | 1713 | cmdedit_set_out_char(' '); |
1711 | } else { /* Insert otherwise */ | 1714 | } else { /* Insert otherwise */ |
1712 | int sc = cursor; | 1715 | int sc = cursor; |
1713 | 1716 | ||
1714 | memmove(command + sc + 1, command + sc, len - sc); | 1717 | memmove(command + sc + 1, command + sc, command_len - sc); |
1715 | command[sc] = c; | 1718 | command[sc] = c; |
1716 | sc++; | 1719 | sc++; |
1717 | /* rewrite from cursor */ | 1720 | /* rewrite from cursor */ |
@@ -1728,35 +1731,12 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1728 | lastWasTab = FALSE; | 1731 | lastWasTab = FALSE; |
1729 | } | 1732 | } |
1730 | 1733 | ||
1731 | #if MAX_HISTORY > 0 | 1734 | if (command_len > 0) |
1732 | /* Handle command history log */ | 1735 | remember_in_history(command); |
1733 | /* cleanup may be saved current command line */ | ||
1734 | if (len > 0) { | ||
1735 | int i = n_history; | ||
1736 | |||
1737 | free(history[MAX_HISTORY]); | ||
1738 | history[MAX_HISTORY] = NULL; | ||
1739 | /* After max history, remove the oldest command */ | ||
1740 | if (i >= MAX_HISTORY) { | ||
1741 | free(history[0]); | ||
1742 | for (i = 0; i < MAX_HISTORY-1; i++) | ||
1743 | history[i] = history[i+1]; | ||
1744 | } | ||
1745 | // Maybe "if (!i || strcmp(history[i-1], command) != 0) ..." | ||
1746 | // (i.e. do not save dups?) | ||
1747 | history[i++] = xstrdup(command); | ||
1748 | cur_history = i; | ||
1749 | n_history = i; | ||
1750 | USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;) | ||
1751 | } | ||
1752 | #else /* MAX_HISTORY == 0 */ | ||
1753 | /* dont put empty line */ | ||
1754 | USE_FEATURE_SH_FANCY_PROMPT(if (len > 0) num_ok_lines++;) | ||
1755 | #endif /* MAX_HISTORY */ | ||
1756 | 1736 | ||
1757 | if (break_out > 0) { | 1737 | if (break_out > 0) { |
1758 | command[len++] = '\n'; | 1738 | command[command_len++] = '\n'; |
1759 | command[len] = '\0'; | 1739 | command[command_len] = '\0'; |
1760 | } | 1740 | } |
1761 | 1741 | ||
1762 | #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION | 1742 | #if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION |
@@ -1764,11 +1744,29 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ]) | |||
1764 | #endif | 1744 | #endif |
1765 | 1745 | ||
1766 | #if ENABLE_FEATURE_SH_FANCY_PROMPT | 1746 | #if ENABLE_FEATURE_SH_FANCY_PROMPT |
1767 | free(cmdedit_prompt); | 1747 | free((char*)cmdedit_prompt); |
1768 | #endif | 1748 | #endif |
1769 | /* restore initial_settings and SIGWINCH handler */ | 1749 | /* restore initial_settings and SIGWINCH handler */ |
1770 | cmdedit_reset_term(); | 1750 | cmdedit_reset_term(); |
1771 | return len; | 1751 | return command_len; |
1752 | } | ||
1753 | |||
1754 | line_input_t *new_line_input_t(int flags) | ||
1755 | { | ||
1756 | line_input_t *n = xzalloc(sizeof(*n)); | ||
1757 | n->flags = flags; | ||
1758 | return n; | ||
1759 | } | ||
1760 | |||
1761 | #else | ||
1762 | |||
1763 | #undef read_line_input | ||
1764 | int read_line_input(const char* prompt, char* command, int maxsize) | ||
1765 | { | ||
1766 | fputs(prompt, stdout); | ||
1767 | fflush(stdout); | ||
1768 | fgets(command, maxsize, stdin); | ||
1769 | return strlen(command); | ||
1772 | } | 1770 | } |
1773 | 1771 | ||
1774 | #endif /* FEATURE_COMMAND_EDITING */ | 1772 | #endif /* FEATURE_COMMAND_EDITING */ |
@@ -1801,13 +1799,13 @@ int main(int argc, char **argv) | |||
1801 | #endif | 1799 | #endif |
1802 | while (1) { | 1800 | while (1) { |
1803 | int l; | 1801 | int l; |
1804 | l = cmdedit_read_input(prompt, buff); | 1802 | l = read_line_input(prompt, buff); |
1805 | if (l <= 0 || buff[l-1] != '\n') | 1803 | if (l <= 0 || buff[l-1] != '\n') |
1806 | break; | 1804 | break; |
1807 | buff[l-1] = 0; | 1805 | buff[l-1] = 0; |
1808 | printf("*** cmdedit_read_input() returned line =%s=\n", buff); | 1806 | printf("*** read_line_input() returned line =%s=\n", buff); |
1809 | } | 1807 | } |
1810 | printf("*** cmdedit_read_input() detect ^D\n"); | 1808 | printf("*** read_line_input() detect ^D\n"); |
1811 | return 0; | 1809 | return 0; |
1812 | } | 1810 | } |
1813 | 1811 | ||