aboutsummaryrefslogtreecommitdiff
path: root/shell/cmdedit.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-01-22 07:21:38 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-01-22 07:21:38 +0000
commit8e1c71529c2bf38a04d4a117e625e59044a0785a (patch)
tree2f115293c25e7ee9307f268ec198e2cf486ff070 /shell/cmdedit.c
parent00cdbd8fc20a4e2e2208f90a2691a3806c931b06 (diff)
downloadbusybox-8e1c71529c2bf38a04d4a117e625e59044a0785a.tar.gz
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.c316
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 @@
#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;
}