diff options
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 | ||