aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-09-29 18:02:37 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-09-29 18:02:37 +0200
commitd17a91db6e0817644445f28515d68b3033388eb4 (patch)
tree427cb084faadaad8a3027564100b4f75e21bb5b8
parent459293b1c536515fbe7fafbae9932aefadb2fbaf (diff)
downloadbusybox-d17a91db6e0817644445f28515d68b3033388eb4.tar.gz
hush: rework input char buffering to allow more than one-deep peek
...this time with actual hush.c changes too :) Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c193
1 files changed, 125 insertions, 68 deletions
diff --git a/shell/hush.c b/shell/hush.c
index d7d152c10..e3dcf2c38 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -462,19 +462,19 @@ static const char *const assignment_flag[] = {
typedef struct in_str {
const char *p;
- /* eof_flag=1: last char in ->p is really an EOF */
- char eof_flag; /* meaningless if ->p == NULL */
- char peek_buf[2];
#if ENABLE_HUSH_INTERACTIVE
smallint promptmode; /* 0: PS1, 1: PS2 */
#endif
+ int peek_buf[2];
int last_char;
FILE *file;
int (*get) (struct in_str *) FAST_FUNC;
int (*peek) (struct in_str *) FAST_FUNC;
+ int (*peek2) (struct in_str *) FAST_FUNC;
} in_str;
#define i_getch(input) ((input)->get(input))
-#define i_peek(input) ((input)->peek(input))
+#define i_peek(input) ((input)->peek(input))
+#define i_peek2(input) ((input)->peek2(input))
/* The descrip member of this structure is only used to make
* debugging output pretty */
@@ -2122,28 +2122,11 @@ static void reinit_unicode_for_hush(void)
}
}
-
/*
- * in_str support
+ * in_str support (strings, and "strings" read from files).
*/
-static int FAST_FUNC static_get(struct in_str *i)
-{
- int ch = *i->p;
- if (ch != '\0') {
- i->p++;
- i->last_char = ch;
- return ch;
- }
- return EOF;
-}
-
-static int FAST_FUNC static_peek(struct in_str *i)
-{
- return *i->p;
-}
#if ENABLE_HUSH_INTERACTIVE
-
static void cmdedit_update_prompt(void)
{
if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
@@ -2157,7 +2140,6 @@ static void cmdedit_update_prompt(void)
if (G.PS2 == NULL)
G.PS2 = "> ";
}
-
static const char *setup_prompt_string(int promptmode)
{
const char *prompt_str;
@@ -2178,8 +2160,7 @@ static const char *setup_prompt_string(int promptmode)
debug_printf("result '%s'\n", prompt_str);
return prompt_str;
}
-
-static void get_user_input(struct in_str *i)
+static int get_user_input(struct in_str *i)
{
int r;
const char *prompt_str;
@@ -2197,11 +2178,14 @@ static void get_user_input(struct in_str *i)
/* catch *SIGINT* etc (^C is handled by read_line_input) */
check_and_run_traps();
} while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
- i->eof_flag = (r < 0);
- if (i->eof_flag) { /* EOF/error detected */
- G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
- G.user_input_buf[1] = '\0';
+ i->p = G.user_input_buf;
+ if (r < 0) {
+ /* EOF/error detected */
+ G.user_input_buf[0] = '\0';
+ i->peek_buf[1] = i->peek_buf[0] = r = EOF;
+ return r;
}
+ return (unsigned char)*i->p++;
# else
do {
G.flag_SIGINT = 0;
@@ -2215,14 +2199,11 @@ static void get_user_input(struct in_str *i)
fputs(prompt_str, stdout);
}
fflush_all();
- G.user_input_buf[0] = r = fgetc(i->file);
- /*G.user_input_buf[1] = '\0'; - already is and never changed */
- } while (G.flag_SIGINT);
- i->eof_flag = (r == EOF);
+ r = fgetc(i->file);
+ } while (G.flag_SIGINT || r == '\0');
+ return r;
# endif
- i->p = G.user_input_buf;
}
-
#endif /* INTERACTIVE */
/* This is the magic location that prints prompts
@@ -2232,28 +2213,36 @@ static int FAST_FUNC file_get(struct in_str *i)
int ch;
/* If there is data waiting, eat it up */
- if (i->p && *i->p) {
-#if ENABLE_HUSH_INTERACTIVE
- take_cached:
-#endif
- ch = *i->p++;
- if (i->eof_flag && !*i->p)
- ch = EOF;
- /* note: ch is never NUL */
- } else {
- /* need to double check i->file because we might be doing something
- * more complicated by now, like sourcing or substituting. */
+ /* peek_buf[] is an int array, not char. Can contain EOF. */
+ ch = i->peek_buf[0];
+ if (ch != '\0') {
+ int ch2 = i->peek_buf[1];
+ i->peek_buf[0] = ch2;
+ if (ch2 == 0) /* very likely, avoid redundant write */
+ goto out;
+ i->peek_buf[1] = 0;
+ goto out;
+ }
+
#if ENABLE_HUSH_INTERACTIVE
- if (G_interactive_fd && i->file == stdin) {
- do {
- get_user_input(i);
- } while (!*i->p); /* need non-empty line */
- i->promptmode = 1; /* PS2 */
- goto take_cached;
- }
-#endif
- do ch = fgetc(i->file); while (ch == '\0');
+ /* This can be stdin, check line editing char[] buffer */
+ if (i->p && *i->p != '\0') {
+ ch = (unsigned char)*i->p++;
+ goto out;
+ }
+ /* It's empty.
+ * If it's interactive stdin, get new line.
+ */
+ if (G_interactive_fd && i->file == stdin) {
+ /* Returns first char (or EOF), the rest are in i->p[] */
+ ch = get_user_input(i);
+ i->promptmode = 1; /* PS2 */
+ goto out;
}
+ /* Not stdin: script file */
+#endif
+ do ch = fgetc(i->file); while (ch == '\0');
+ out:
debug_printf("file_get: got '%c' %d\n", ch, ch);
i->last_char = ch;
return ch;
@@ -2265,26 +2254,82 @@ static int FAST_FUNC file_get(struct in_str *i)
static int FAST_FUNC file_peek(struct in_str *i)
{
int ch;
- if (i->p && *i->p) {
- if (i->eof_flag && !i->p[1])
- return EOF;
- return *i->p;
- /* note: ch is never NUL */
- }
+
+ /* peek_buf[] is an int array, not char. Can contain EOF. */
+ ch = i->peek_buf[0];
+ if (ch != '\0')
+ return ch;
+
+#if ENABLE_HUSH_INTERACTIVE
+ /* This can be stdin, check line editing char[] buffer */
+ if (i->p && *i->p != '\0')
+ return (unsigned char)*i->p;
+#endif
+
do ch = fgetc(i->file); while (ch == '\0');
- i->eof_flag = (ch == EOF);
i->peek_buf[0] = ch;
- i->peek_buf[1] = '\0';
- i->p = i->peek_buf;
+ i->peek_buf[1] = 0;
debug_printf("file_peek: got '%c' %d\n", ch, ch);
return ch;
}
+/* Only ever called if i_peek() was called, and did not return EOF */
+static int FAST_FUNC file_peek2(struct in_str *i)
+{
+ int ch;
+
+ /* peek_buf[] is an int array, not char. Can contain EOF. */
+ ch = i->peek_buf[0];
+ if (ch != 0) {
+ /* peek_buf[] is not empty. Is there 2nd char? */
+ ch = i->peek_buf[1];
+ if (ch == 0) {
+ /* We did not read it yet, get it now */
+ do ch = fgetc(i->file); while (ch == '\0');
+ i->peek_buf[1] = ch;
+ }
+ goto out;
+ }
+
+#if ENABLE_HUSH_INTERACTIVE
+ /* This can be stdin, check line editing char[] buffer */
+ if (i->p && i->p[0] != '\0')
+ ch = i->p[1];
+#endif
+ out:
+ debug_printf("file_peek2: got '%c' %d\n", ch, ch);
+ return ch;
+}
+
+static int FAST_FUNC static_get(struct in_str *i)
+{
+ int ch = *i->p;
+ if (ch != '\0') {
+ i->p++;
+ i->last_char = ch;
+ return ch;
+ }
+ return EOF;
+}
+
+static int FAST_FUNC static_peek(struct in_str *i)
+{
+ /* Doesn't report EOF on NUL. None of the callers care. */
+ return *i->p;
+}
+
+/* Only ever called if i_peek() was called, and did not return EOF */
+static int FAST_FUNC static_peek2(struct in_str *i)
+{
+ return i->p[1];
+}
+
static void setup_file_in_str(struct in_str *i, FILE *f)
{
memset(i, 0, sizeof(*i));
- i->peek = file_peek;
i->get = file_get;
+ i->peek = file_peek;
+ i->peek2 = file_peek2;
/* i->promptmode = 0; - PS1 (memset did it) */
i->file = f;
/* i->p = NULL; */
@@ -2293,11 +2338,11 @@ static void setup_file_in_str(struct in_str *i, FILE *f)
static void setup_string_in_str(struct in_str *i, const char *s)
{
memset(i, 0, sizeof(*i));
- i->peek = static_peek;
i->get = static_get;
+ i->peek = static_peek;
+ i->peek2 = static_peek2;
/* i->promptmode = 0; - PS1 (memset did it) */
i->p = s;
- /* i->eof_flag = 0; */
}
@@ -4040,9 +4085,21 @@ static int parse_dollar(o_string *as_string,
debug_printf_parse(": '%c'\n", ch);
o_addchr(dest, ch | quote_mask);
quote_mask = 0;
+ next_ch:
ch = i_peek(input);
- if (!isalnum(ch) && ch != '_')
+ if (!isalnum(ch) && ch != '_') {
+ if (ch == '\\') {
+ /* If backslash+newline, skip it */
+ int ch2 = i_peek2(input);
+ if (ch2 == '\n') {
+ i_getch(input);
+ i_getch(input);
+ goto next_ch;
+ }
+ }
+ /* End of variable name reached */
break;
+ }
ch = i_getch(input);
nommu_addchr(as_string, ch);
}