+
+static inline void get_next_history(struct history **hp)
+{
+ get_previous_history(hp, (*hp)->n);
+}
+
+enum {
+ ESC = 27,
+ DEL = 127,
+};
+
+
+/*
+ * This function is used to grab a character buffer
+ * from the input file descriptor and allows you to
+ * a string with full command editing (sortof like
+ * a mini readline).
+ *
+ * The following standard commands are not implemented:
+ * ESC-b -- Move back one word
+ * ESC-f -- Move forward one word
+ * ESC-d -- Delete back one word
+ * ESC-h -- Delete forward one word
+ * CTL-t -- Transpose two characters
+ *
+ * Furthermore, the "vi" command editing keys are not implemented.
+ *
+ */
+extern void cmdedit_read_input(char *prompt, char command[BUFSIZ])
+{
+
+ int inputFd = fileno(stdin);
+
+ int break_out = 0;
+ int lastWasTab = FALSE;
+ char c = 0;
+ struct history *hp = his_end;
+
+ /* prepare before init handlers */
+ cmdedit_y = 0; /* quasireal y, not true work if line > xt*yt */
+ len = 0;
+ command_ps = command;
+
+ if (new_settings.c_cc[VMIN] == 0) { /* first call */
+
+ getTermSettings(inputFd, (void *) &initial_settings);
+ memcpy(&new_settings, &initial_settings, sizeof(struct termios));
+
+ new_settings.c_cc[VMIN] = 1;
+ new_settings.c_cc[VTIME] = 0;
+ new_settings.c_cc[VINTR] = _POSIX_VDISABLE; /* Turn off CTRL-C, so we can trap it */
+ new_settings.c_lflag &= ~ICANON; /* unbuffered input */
+ new_settings.c_lflag &= ~(ECHO | ECHOCTL | ECHONL); /* Turn off echoing */
+ }
+
+ command[0] = 0;
+
+ setTermSettings(inputFd, (void *) &new_settings);
+ handlers_sets |= SET_RESET_TERM;
+
+ /* Print out the command prompt */
+ parse_prompt(prompt);
+ /* Now initialize things */
+ cmdedit_init();
+
+ while (1) {
+
+ fflush(stdout); /* buffered out to fast */
+
+ if (read(inputFd, &c, 1) < 1)
+ return;
+
+ switch (c) {
+ case '\n':
+ case '\r':
+ /* Enter */
+ goto_new_line();
+ break_out = 1;
+ break;
+ case 1:
+ /* Control-a -- Beginning of line */
+ input_backward(cursor);
+ break;
+ case 2:
+ /* Control-b -- Move back one character */
+ input_backward(1);
+ break;
+ case 3:
+ /* Control-c -- stop gathering input */
+
+ /* Link into lash to reset context to 0 on ^C and such */
+ shell_context = 0;
+
+ /* Go to the next line */
+ goto_new_line();
+ command[0] = 0;
+
+ return;
+ case 4:
+ /* Control-d -- Delete one character, or exit
+ * if the len=0 and no chars to delete */
+ if (len == 0) {
+ printf("exit");
+ clean_up_and_die(0);
+ } else {
+ input_delete();
+ }
+ break;
+ case 5:
+ /* Control-e -- End of line */
+ input_end();
+ break;
+ case 6:
+ /* Control-f -- Move forward one character */
+ input_forward();
+ break;
+ case '\b':
+ case DEL:
+ /* Control-h and DEL */
+ input_backspace();
+ break;
+ case '\t':
+#ifdef BB_FEATURE_SH_TAB_COMPLETION
+ input_tab(&lastWasTab);
+#endif
+ break;
+ case 14:
+ /* Control-n -- Get next command in history */
+ if (hp && hp->n && hp->n->s) {
+ get_next_history(&hp);
+ goto rewrite_line;
+ } else {
+ beep();
+ }
+ break;
+ case 16:
+ /* Control-p -- Get previous command from history */
+ if (hp && hp->p) {
+ get_previous_history(&hp, hp->p);
+ goto rewrite_line;
+ } else {
+ beep();
+ }
+ break;
+ case 21:
+ /* Control-U -- Clear line before cursor */
+ if (cursor) {
+ strcpy(command, command + cursor);
+ redraw(cmdedit_y, len -= cursor);
+ }
+ break;
+
+ case ESC:{
+ /* escape sequence follows */
+ if (read(inputFd, &c, 1) < 1)
+ return;
+ /* different vt100 emulations */
+ if (c == '[' || c == 'O') {
+ if (read(inputFd, &c, 1) < 1)
+ return;
+ }
+ switch (c) {
+#ifdef BB_FEATURE_SH_TAB_COMPLETION
+ case '\t': /* Alt-Tab */
+
+ input_tab(&lastWasTab);
+ break;
+#endif
+ case 'A':
+ /* Up Arrow -- Get previous command from history */
+ if (hp && hp->p) {
+ get_previous_history(&hp, hp->p);
+ goto rewrite_line;
+ } else {
+ beep();
+ }
+ break;
+ case 'B':
+ /* Down Arrow -- Get next command in history */
+ if (hp && hp->n && hp->n->s) {
+ get_next_history(&hp);
+ goto rewrite_line;
+ } else {
+ beep();
+ }
+ break;
+
+ /* Rewrite the line with the selected history item */
+ rewrite_line:
+ /* change command */
+ len = strlen(strcpy(command, hp->s));
+ /* redraw and go to end line */
+ redraw(cmdedit_y, 0);
+ break;
+ case 'C':
+ /* Right Arrow -- Move forward one character */
+ input_forward();
+ break;
+ case 'D':
+ /* Left Arrow -- Move back one character */
+ input_backward(1);
+ break;
+ case '3':
+ /* Delete */
+ input_delete();
+ break;
+ case '1':
+ case 'H':
+ /* Home (Ctrl-A) */
+ input_backward(cursor);
+ break;
+ case '4':
+ case 'F':
+ /* End (Ctrl-E) */
+ input_end();
+ break;
+ default:
+ if (!(c >= '1' && c <= '9'))
+ c = 0;
+ beep();
+ }
+ if (c >= '1' && c <= '9')
+ do
+ if (read(inputFd, &c, 1) < 1)
+ return;
+ while (c != '~');
+ break;
+ }
+
+ default: /* If it's regular input, do the normal thing */
+#ifdef BB_FEATURE_NONPRINTABLE_INVERSE_PUT
+ /* Control-V -- Add non-printable symbol */
+ if (c == 22) {
+ if (read(inputFd, &c, 1) < 1)
+ return;
+ if (c == 0) {
+ beep();
+ break;
+ }
+ } else
+#endif
+ if (!isprint(c)) /* Skip non-printable characters */
+ break;
+
+ if (len >= (BUFSIZ - 2)) /* Need to leave space for enter */
+ break;
+
+ len++;
+
+ if (cursor == (len - 1)) { /* Append if at the end of the line */
+ *(command + cursor) = c;
+ *(command + cursor + 1) = 0;
+ cmdedit_set_out_char(0);
+ } else { /* Insert otherwise */
+ int sc = cursor;
+
+ memmove(command + sc + 1, command + sc, len - sc);
+ *(command + sc) = c;
+ sc++;
+ /* rewrite from cursor */
+ input_end();
+ /* to prev x pos + 1 */
+ input_backward(cursor - sc);
+ }
+
+ break;
+ }
+ if (break_out) /* Enter is the command terminator, no more input. */
+ break;
+
+ if (c != '\t')
+ lastWasTab = FALSE;
+ }
+
+ setTermSettings(inputFd, (void *) &initial_settings);
+ handlers_sets &= ~SET_RESET_TERM;
+
+ /* Handle command history log */
+ if (len) { /* no put empty line */
+
+ struct history *h = his_end;
+ char *ss;
+
+ ss = xstrdup(command); /* duplicate */
+
+ if (h == 0) {
+ /* No previous history -- this memory is never freed */
+ h = his_front = xmalloc(sizeof(struct history));
+ h->n = xmalloc(sizeof(struct history));
+
+ h->p = NULL;
+ h->s = ss;
+ h->n->p = h;
+ h->n->n = NULL;
+ h->n->s = NULL;
+ his_end = h->n;
+ history_counter++;
+ } else {
+ /* Add a new history command -- this memory is never freed */
+ h->n = xmalloc(sizeof(struct history));
+
+ h->n->p = h;
+ h->n->n = NULL;
+ h->n->s = NULL;
+ h->s = ss;
+ his_end = h->n;
+
+ /* After max history, remove the oldest command */
+ if (history_counter >= MAX_HISTORY) {
+
+ struct history *p = his_front->n;
+
+ p->p = NULL;
+ free(his_front->s);
+ free(his_front);
+ his_front = p;
+ } else {
+ history_counter++;
+ }
+ }
+#if defined(BB_FEATURE_BASH_STYLE_PROMT)
+ num_ok_lines++;
+#endif
+ }
+ command[len++] = '\n'; /* set '\n' */
+ command[len] = 0;
+#if defined(BB_FEATURE_CLEAN_UP) && defined(BB_FEATURE_SH_TAB_COMPLETION)
+ input_tab(0); /* strong free */
+#endif
+#if defined(BB_FEATURE_BASH_STYLE_PROMT)
+ free(cmdedit_prompt);
+#endif
+ return;
+}
+
+
+/* Undo the effects of cmdedit_init(). */
+extern void cmdedit_terminate(void)
+{
+ cmdedit_reset_term();
+ if ((handlers_sets & SET_TERM_HANDLERS) != 0) {
+ signal(SIGKILL, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGWINCH, SIG_DFL);
+ handlers_sets &= ~SET_TERM_HANDLERS;
+ }
+}
+
+#endif /* BB_FEATURE_SH_COMMAND_EDITING */
+
+
+#ifdef TEST
+
+unsigned int shell_context;
+
+int main(int argc, char **argv)
+{
+ char buff[BUFSIZ];
+ char *prompt =
+#if defined(BB_FEATURE_BASH_STYLE_PROMT)
+ "\\[\\033[32;1m\\]\\u@\\[\\033[33;1m\\]\\h:\
+\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] \
+\\!\\[\\033[36;1m\\]\\$ \\[\\033[0m\\]";
+#else
+ "% ";
+#endif
+
+ shell_context = 1;
+ do {
+ cmdedit_read_input(prompt, buff);
+ printf("*** cmdedit_read_input() returned line =%s=\n", buff);
+ } while (shell_context);
+ printf("*** cmdedit_read_input() detect ^C\n");
+ return 0;
+}
+
+#endif /* TEST */