summaryrefslogtreecommitdiff
path: root/shell/cmdedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'shell/cmdedit.c')
-rw-r--r--shell/cmdedit.c316
1 files changed, 157 insertions, 159 deletions
diff --git a/shell/cmdedit.c b/shell/cmdedit.c
index a1432af..554a4eb 100644
--- a/shell/cmdedit.c
+++ b/shell/cmdedit.c
@@ -30,7 +30,6 @@
#include <sys/ioctl.h>
#include "busybox.h"
-#include "cmdedit.h"
/* FIXME: obsolete CONFIG item? */
@@ -51,7 +50,6 @@
/* Entire file (except TESTing part) sits inside this #if */
#if ENABLE_FEATURE_COMMAND_EDITING
-
#if ENABLE_LOCALE_SUPPORT
#define Isprint(c) isprint(c)
#else
@@ -61,29 +59,21 @@
#define ENABLE_FEATURE_GETUSERNAME_AND_HOMEDIR \
(ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION || ENABLE_FEATURE_SH_FANCY_PROMPT)
-/* Maximum length of command line history */
-#if !ENABLE_FEATURE_COMMAND_HISTORY
-#define MAX_HISTORY 15
-#else
-#define MAX_HISTORY (CONFIG_FEATURE_COMMAND_HISTORY + 0)
-#endif
+static line_input_t *state;
-/* Current termios and the previous termios before starting sh */
static struct termios initial_settings, new_settings;
-static
-volatile unsigned cmdedit_termw = 80; /* actual terminal width */
-
+static volatile unsigned cmdedit_termw = 80; /* actual terminal width */
static int cmdedit_x; /* real x terminal position */
static int cmdedit_y; /* pseudoreal y terminal position */
static int cmdedit_prmt_len; /* length of prompt (without colors etc) */
-static int cursor;
-static int len;
+static unsigned cursor;
+static unsigned command_len;
static char *command_ps;
-static SKIP_FEATURE_SH_FANCY_PROMPT(const) char *cmdedit_prompt;
+static const char *cmdedit_prompt;
#if ENABLE_FEATURE_SH_FANCY_PROMPT
static char *hostname_buf;
@@ -142,7 +132,7 @@ static void cmdedit_set_out_char(int next_char)
/* Move to end of line (by printing all chars till the end) */
static void input_end(void)
{
- while (cursor < len)
+ while (cursor < command_len)
cmdedit_set_out_char(' ');
}
@@ -200,7 +190,7 @@ static void input_backward(unsigned num)
static void put_prompt(void)
{
out1str(cmdedit_prompt);
- cmdedit_x = cmdedit_prmt_len; /* count real x terminal position */
+ cmdedit_x = cmdedit_prmt_len;
cursor = 0;
// Huh? what if cmdedit_prmt_len >= width?
cmdedit_y = 0; /* new quasireal y */
@@ -231,7 +221,7 @@ static void input_delete(int save)
{
int j = cursor;
- if (j == len)
+ if (j == command_len)
return;
#if ENABLE_FEATURE_COMMAND_EDITING_VI
@@ -249,7 +239,7 @@ static void input_delete(int save)
#endif
strcpy(command_ps + j, command_ps + j + 1);
- len--;
+ command_len--;
input_end(); /* rewrite new line */
cmdedit_set_out_char(' '); /* erase char */
input_backward(cursor - j); /* back to old pos cursor */
@@ -285,7 +275,7 @@ static void input_backspace(void)
/* Move forward one character */
static void input_forward(void)
{
- if (cursor < len)
+ if (cursor < command_len)
cmdedit_set_out_char(command_ps[cursor + 1]);
}
@@ -372,54 +362,50 @@ enum {
FIND_FILE_ONLY = 2,
};
-#if ENABLE_ASH
-const char *cmdedit_path_lookup;
-#endif
static int path_parse(char ***p, int flags)
{
int npth;
const char *tmp;
-#if ENABLE_ASH
- const char *pth = cmdedit_path_lookup;
-#else
- const char *pth = getenv("PATH")
-#endif
+ const char *pth;
+ char **res;
/* if not setenv PATH variable, to search cur dir "." */
if (flags != FIND_EXE_ONLY)
return 1;
+
+ if (state->flags & WITH_PATH_LOOKUP)
+ pth = state->path_lookup;
+ else
+ pth = getenv("PATH");
/* PATH=<empty> or PATH=:<empty> */
if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
return 1;
tmp = pth;
- npth = 0;
-
+ npth = 1; /* path component count */
while (1) {
- npth++; /* count words is + 1 count ':' */
tmp = strchr(tmp, ':');
if (!tmp)
break;
if (*++tmp == '\0')
break; /* :<empty> */
+ npth++;
}
- *p = xmalloc(npth * sizeof(char *));
-
+ res = xmalloc(npth * sizeof(char*));
+ res[0] = xstrdup(pth);
tmp = pth;
- (*p)[0] = xstrdup(tmp);
- npth = 1; /* count words is + 1 count ':' */
-
+ npth = 1;
while (1) {
tmp = strchr(tmp, ':');
if (!tmp)
break;
- (*p)[0][(tmp - pth)] = 0; /* ':' -> '\0' */
- if (*++tmp == 0)
- break; /* :<empty> */
- (*p)[npth++] = &(*p)[0][(tmp - pth)]; /* p[next]=p[0][&'\0'+1] */
+ *tmp++ = '\0'; /* ':' -> '\0' */
+ if (*tmp == '\0')
+ break; /* :<empty> */
+ res[npth++] = tmp;
}
-
+ *p = res;
return npth;
}
@@ -742,6 +728,9 @@ static int match_compare(const void *a, const void *b)
/* Do TAB completion */
static void input_tab(int *lastWasTab)
{
+ if (!(state->flags & TAB_COMPLETION))
+ return;
+
if (!*lastWasTab) {
char *tmp, *tmp1;
int len_found;
@@ -764,13 +753,13 @@ static void input_tab(int *lastWasTab)
#if ENABLE_FEATURE_COMMAND_USERNAME_COMPLETION
/* If the word starts with `~' and there is no slash in the word,
* then try completing this word as a username. */
-
- if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
- username_tab_completion(matchBuf, NULL);
- if (!matches)
+ if (state->flags & USERNAME_COMPLETION)
+ if (matchBuf[0] == '~' && strchr(matchBuf, '/') == 0)
+ username_tab_completion(matchBuf, NULL);
#endif
/* Try to match any executable in our path and everything
* in the current working directory */
+ if (!matches)
exe_n_cwd_tab_completion(matchBuf, find_type);
/* Sort, then remove any duplicates found */
if (matches) {
@@ -855,51 +844,48 @@ static void input_tab(int *lastWasTab)
}
}
+#else
+#define input_tab(a) ((void)0)
#endif /* FEATURE_COMMAND_TAB_COMPLETION */
#if MAX_HISTORY > 0
-static char *history[MAX_HISTORY+1]; /* history + current */
-/* saved history lines */
-static int n_history;
-/* current pointer to history line */
-static int cur_history;
-
+/* state->flags is already checked to be nonzero */
static void get_previous_history(void)
{
- if (command_ps[0] != '\0' || history[cur_history] == NULL) {
- free(history[cur_history]);
- history[cur_history] = xstrdup(command_ps);
+ if (command_ps[0] != '\0' || state->history[state->cur_history] == NULL) {
+ free(state->history[state->cur_history]);
+ state->history[state->cur_history] = xstrdup(command_ps);
}
- cur_history--;
+ state->cur_history--;
}
static int get_next_history(void)
{
- int ch = cur_history;
-
- if (ch < n_history) {
- get_previous_history(); /* save the current history line */
- cur_history = ch + 1;
- return cur_history;
- } else {
- beep();
- return 0;
+ if (state->flags & DO_HISTORY) {
+ int ch = state->cur_history;
+ if (ch < state->cnt_history) {
+ get_previous_history(); /* save the current history line */
+ state->cur_history = ch + 1;
+ return state->cur_history;
+ }
}
+ beep();
+ return 0;
}
#if ENABLE_FEATURE_COMMAND_SAVEHISTORY
+/* state->flags is already checked to be nonzero */
void load_history(const char *fromfile)
{
FILE *fp;
int hi;
/* cleanup old */
-
- for (hi = n_history; hi > 0;) {
+ for (hi = state->cnt_history; hi > 0;) {
hi--;
- free(history[hi]);
+ free(state->history[hi]);
}
fp = fopen(fromfile, "r");
@@ -917,29 +903,62 @@ void load_history(const char *fromfile)
free(hl);
continue;
}
- history[hi++] = hl;
+ state->history[hi++] = hl;
}
fclose(fp);
}
- cur_history = n_history = hi;
+ state->cur_history = state->cnt_history = hi;
}
+/* state->flags is already checked to be nonzero */
void save_history(const char *tofile)
{
- FILE *fp = fopen(tofile, "w");
+ FILE *fp;
+ fp = fopen(tofile, "w");
if (fp) {
int i;
- for (i = 0; i < n_history; i++) {
- fprintf(fp, "%s\n", history[i]);
+ for (i = 0; i < state->cnt_history; i++) {
+ fprintf(fp, "%s\n", state->history[i]);
}
fclose(fp);
}
}
+#else
+#define load_history(a) ((void)0)
+#define save_history(a) ((void)0)
#endif /* FEATURE_COMMAND_SAVEHISTORY */
-#endif /* MAX_HISTORY > 0 */
+static void remember_in_history(const char *str)
+{
+ int i;
+
+ if (!(state->flags & DO_HISTORY))
+ return;
+
+ i = state->cnt_history;
+ free(state->history[MAX_HISTORY]);
+ state->history[MAX_HISTORY] = NULL;
+ /* After max history, remove the oldest command */
+ if (i >= MAX_HISTORY) {
+ free(state->history[0]);
+ for (i = 0; i < MAX_HISTORY-1; i++)
+ state->history[i] = state->history[i+1];
+ }
+// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
+// (i.e. do not save dups?)
+ state->history[i++] = xstrdup(str);
+ state->cur_history = i;
+ state->cnt_history = i;
+ if (state->flags & SAVE_HISTORY)
+ save_history(state->hist_file);
+ USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;)
+}
+
+#else /* MAX_HISTORY == 0 */
+#define remember_in_history(a) ((void)0)
+#endif /* MAX_HISTORY */
/*
@@ -960,13 +979,6 @@ void save_history(const char *tofile)
*/
#if ENABLE_FEATURE_COMMAND_EDITING_VI
-static int vi_mode;
-
-void setvimode(int viflag)
-{
- vi_mode = viflag;
-}
-
static void
vi_Word_motion(char *command, int eat)
{
@@ -1058,13 +1070,11 @@ vi_back_motion(char *command)
input_backward(1);
}
}
-#else
-enum { vi_mode = 0 };
#endif
/*
- * cmdedit_read_input and its helpers
+ * read_line_input and its helpers
*/
#if !ENABLE_FEATURE_SH_FANCY_PROMPT
@@ -1190,7 +1200,7 @@ static void parse_prompt(const char *prmt_ptr)
cmdedit_prmt_len += cur_prmt_len;
prmt_mem_ptr = strcat(xrealloc(prmt_mem_ptr, prmt_len+1), pbuf);
}
- if (pwd_buf!=(char *)bb_msg_unknown)
+ if (pwd_buf != (char *)bb_msg_unknown)
free(pwd_buf);
cmdedit_prompt = prmt_mem_ptr;
put_prompt();
@@ -1217,7 +1227,7 @@ static void cmdedit_setwidth(unsigned w, int redraw_flg)
/* new y for current cursor */
int new_y = (cursor + cmdedit_prmt_len) / w;
/* redraw */
- redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), len - cursor);
+ redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
fflush(stdout);
}
}
@@ -1275,9 +1285,10 @@ static void cmdedit_init(void)
#undef CTRL
#define CTRL(a) ((a) & ~0x40)
-
-int cmdedit_read_input(char *prompt, char command[BUFSIZ])
+int read_line_input(const char* prompt, char* command, int maxsize, line_input_t *st)
{
+ static const int null_flags;
+
int lastWasTab = FALSE;
unsigned int ic;
unsigned char c;
@@ -1286,18 +1297,28 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
smallint vi_cmdmode = 0;
smalluint prevc;
#endif
+
+// FIXME: audit & improve this
+ if (maxsize > BUFSIZ)
+ maxsize = BUFSIZ;
+
+ /* With null flags, no other fields are ever used */
+ state = st ? st : (line_input_t*) &null_flags;
+ if (state->flags & SAVE_HISTORY)
+ load_history(state->hist_file);
+
/* prepare before init handlers */
cmdedit_y = 0; /* quasireal y, not true if line > xt*yt */
- len = 0;
+ command_len = 0;
command_ps = command;
command[0] = '\0';
getTermSettings(0, (void *) &initial_settings);
- memcpy(&new_settings, &initial_settings, sizeof(struct termios));
+ memcpy(&new_settings, &initial_settings, sizeof(new_settings));
new_settings.c_lflag &= ~ICANON; /* unbuffered input */
/* Turn off echoing and CTRL-C, so we can trap it */
new_settings.c_lflag &= ~(ECHO | ECHONL | ISIG);
- /* Hmm, in linux c_cc[] not parsed if set ~ICANON */
+ /* Hmm, in linux c_cc[] is not parsed if ICANON is off */
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
/* Turn off CTRL-C, so we can trap it */
@@ -1354,34 +1375,18 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
vi_case(CTRL('C')|vbit:)
/* Control-c -- stop gathering input */
goto_new_line();
-#if !ENABLE_ASH
- command[0] = '\0';
- len = 0;
- lastWasTab = FALSE;
- put_prompt();
-#else
- len = 0;
- break_out = -1; /* to control traps */
-#endif
+ command_len = 0;
+ break_out = -1; /* "do not append '\n'" */
break;
case CTRL('D'):
/* Control-d -- Delete one character, or exit
* if the len=0 and no chars to delete */
- if (len == 0) {
+ if (command_len == 0) {
errno = 0;
prepare_to_die:
-// So, our API depends on whether we have ash compiled in or not? Crap...
-#if !ENABLE_ASH
- printf("exit");
- goto_new_line();
- /* cmdedit_reset_term() called in atexit */
-// FIXME. this is definitely not good
- exit(EXIT_SUCCESS);
-#else
/* to control stopped jobs */
- break_out = len = -1;
+ break_out = command_len = -1;
break;
-#endif
}
input_delete(0);
break;
@@ -1407,23 +1412,21 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
break;
case '\t':
-#if ENABLE_FEATURE_COMMAND_TAB_COMPLETION
input_tab(&lastWasTab);
-#endif
break;
#if ENABLE_FEATURE_EDITING_FANCY_KEYS
case CTRL('K'):
/* Control-k -- clear to end of line */
command[cursor] = 0;
- len = cursor;
+ command_len = cursor;
printf("\033[J");
break;
case CTRL('L'):
vi_case(CTRL('L')|vbit:)
/* Control-l -- clear screen */
printf("\033[H");
- redraw(0, len - cursor);
+ redraw(0, command_len - cursor);
break;
#endif
@@ -1439,12 +1442,11 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
vi_case(CTRL('P')|vbit:)
vi_case('k'|vbit:)
/* Control-p -- Get previous command from history */
- if (cur_history > 0) {
+ if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
get_previous_history();
goto rewrite_line;
- } else {
- beep();
}
+ beep();
break;
#endif
@@ -1454,7 +1456,8 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
/* Control-U -- Clear line before cursor */
if (cursor) {
strcpy(command, command + cursor);
- redraw(cmdedit_y, len -= cursor);
+ command_len -= cursor;
+ redraw(cmdedit_y, command_len);
}
break;
#endif
@@ -1571,7 +1574,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
break;
case '$': /* "d$", "c$" */
clear_to_eol:
- while (cursor < len)
+ while (cursor < command_len)
input_delete(1);
break;
}
@@ -1599,7 +1602,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
case '\x1b': /* ESC */
#if ENABLE_FEATURE_COMMAND_EDITING_VI
- if (vi_mode) {
+ if (state->flags & VI_MODE) {
/* ESC: insert mode --> command mode */
vi_cmdmode = 1;
input_backward(1);
@@ -1634,7 +1637,7 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
#if MAX_HISTORY > 0
case 'A':
/* Up Arrow -- Get previous command from history */
- if (cur_history > 0) {
+ if ((state->flags & DO_HISTORY) && state->cur_history > 0) {
get_previous_history();
goto rewrite_line;
}
@@ -1647,9 +1650,9 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
rewrite_line:
/* Rewrite the line with the selected history item */
/* change command */
- len = strlen(strcpy(command, history[cur_history]));
+ command_len = strlen(strcpy(command, state->history[state->cur_history]));
/* redraw and go to eol (bol, in vi */
- redraw(cmdedit_y, vi_mode ? 9999 : 0);
+ redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
break;
#endif
case 'C':
@@ -1700,18 +1703,18 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
if (!Isprint(c)) /* Skip non-printable characters */
break;
- if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
+ if (command_len >= (maxsize - 2)) /* Need to leave space for enter */
break;
- len++;
- if (cursor == (len - 1)) { /* Append if at the end of the line */
+ command_len++;
+ if (cursor == (command_len - 1)) { /* Append if at the end of the line */
command[cursor] = c;
command[cursor+1] = '\0';
cmdedit_set_out_char(' ');
} else { /* Insert otherwise */
int sc = cursor;
- memmove(command + sc + 1, command + sc, len - sc);
+ memmove(command + sc + 1, command + sc, command_len - sc);
command[sc] = c;
sc++;
/* rewrite from cursor */
@@ -1728,35 +1731,12 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
lastWasTab = FALSE;
}
-#if MAX_HISTORY > 0
- /* Handle command history log */
- /* cleanup may be saved current command line */
- if (len > 0) {
- int i = n_history;
-
- free(history[MAX_HISTORY]);
- history[MAX_HISTORY] = NULL;
- /* After max history, remove the oldest command */
- if (i >= MAX_HISTORY) {
- free(history[0]);
- for (i = 0; i < MAX_HISTORY-1; i++)
- history[i] = history[i+1];
- }
-// Maybe "if (!i || strcmp(history[i-1], command) != 0) ..."
-// (i.e. do not save dups?)
- history[i++] = xstrdup(command);
- cur_history = i;
- n_history = i;
- USE_FEATURE_SH_FANCY_PROMPT(num_ok_lines++;)
- }
-#else /* MAX_HISTORY == 0 */
- /* dont put empty line */
- USE_FEATURE_SH_FANCY_PROMPT(if (len > 0) num_ok_lines++;)
-#endif /* MAX_HISTORY */
+ if (command_len > 0)
+ remember_in_history(command);
if (break_out > 0) {
- command[len++] = '\n';
- command[len] = '\0';
+ command[command_len++] = '\n';
+ command[command_len] = '\0';
}
#if ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_COMMAND_TAB_COMPLETION
@@ -1764,11 +1744,29 @@ int cmdedit_read_input(char *prompt, char command[BUFSIZ])
#endif
#if ENABLE_FEATURE_SH_FANCY_PROMPT
- free(cmdedit_prompt);
+ free((char*)cmdedit_prompt);
#endif
/* restore initial_settings and SIGWINCH handler */
cmdedit_reset_term();
- return len;
+ return command_len;
+}
+
+line_input_t *new_line_input_t(int flags)
+{
+ line_input_t *n = xzalloc(sizeof(*n));
+ n->flags = flags;
+ return n;
+}
+
+#else
+
+#undef read_line_input
+int read_line_input(const char* prompt, char* command, int maxsize)
+{
+ fputs(prompt, stdout);
+ fflush(stdout);
+ fgets(command, maxsize, stdin);
+ return strlen(command);
}
#endif /* FEATURE_COMMAND_EDITING */
@@ -1801,13 +1799,13 @@ int main(int argc, char **argv)
#endif
while (1) {
int l;
- l = cmdedit_read_input(prompt, buff);
+ l = read_line_input(prompt, buff);
if (l <= 0 || buff[l-1] != '\n')
break;
buff[l-1] = 0;
- printf("*** cmdedit_read_input() returned line =%s=\n", buff);
+ printf("*** read_line_input() returned line =%s=\n", buff);
}
- printf("*** cmdedit_read_input() detect ^D\n");
+ printf("*** read_line_input() detect ^D\n");
return 0;
}