diff options
author | Denis Vlasenko | 2007-05-11 12:56:43 +0000 |
---|---|---|
committer | Denis Vlasenko | 2007-05-11 12:56:43 +0000 |
commit | 3e9aaae5dc384ae070c49507a92b1375397954cd (patch) | |
tree | 048661e5d8448c8cb39a4453fe36f4e6c8746384 /shell/hush.c | |
parent | e0a336747c2061d0d555c4e15287b513831d2947 (diff) | |
download | busybox-3e9aaae5dc384ae070c49507a92b1375397954cd.zip busybox-3e9aaae5dc384ae070c49507a92b1375397954cd.tar.gz |
hush: fix bug in interactive shell introduced yesterday
hush: fix `process subst` (2 bugs)
NB: will delete and re-add hush_test in order to change file modes
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 88 |
1 files changed, 46 insertions, 42 deletions
diff --git a/shell/hush.c b/shell/hush.c index 32cd65c..04fb00d 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -317,10 +317,10 @@ typedef struct { /* I can almost use ordinary FILE *. Is open_memstream() universally * available? Where is it documented? */ struct in_str { - union { - const char *p; - int cached_ch; - }; + 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 int __promptme; int promptmode; @@ -976,7 +976,7 @@ static int b_check_space(o_string *o, int len) static int b_addchr(o_string *o, int ch) { - debug_printf("b_addchr: %c %d %p\n", ch, o->length, o); + debug_printf("b_addchr: '%c' o->lengtt=%d o=%p\n", ch, o->length, o); if (b_check_space(o, 1)) return B_NOSPAC; o->data[o->length] = ch; @@ -1079,12 +1079,13 @@ static const char* setup_prompt_string(int promptmode) static line_input_t *line_input_state; #endif -static int get_user_input(struct in_str *i) +static void get_user_input(struct in_str *i) { static char the_command[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; int r; const char *prompt_str; + prompt_str = setup_prompt_string(i->promptmode); #if ENABLE_FEATURE_EDITING /* @@ -1094,15 +1095,19 @@ static int get_user_input(struct in_str *i) ** child processes (rob@sysgo.de) */ r = read_line_input(prompt_str, the_command, BUFSIZ-1, line_input_state); + i->eof_flag = (r < 0); + if (i->eof_flag) { /* EOF/error detected */ + the_command[0] = EOF; /* yes, it will be truncated, it's ok */ + the_command[1] = '\0'; + } #else fputs(prompt_str, stdout); fflush(stdout); the_command[0] = r = fgetc(i->file); /*the_command[1] = '\0'; - already is and never changed */ + i->eof_flag = (r == EOF); #endif - fflush(stdout); i->p = the_command; - return r; /* < 0 == EOF. Not meaningful otherwise */ } #endif /* INTERACTIVE */ @@ -1112,33 +1117,30 @@ static int file_get(struct in_str *i) { int ch; - ch = 0; /* If there is data waiting, eat it up */ - if (i->cached_ch) { - ch = i->cached_ch ^ 0x100; - if (ch != EOF) - i->cached_ch = 0; + if (i->p && *i->p) { + take_cached: + ch = *i->p++; + if (i->eof_flag && !*i->p) + ch = EOF; } else { /* need to double check i->file because we might be doing something * more complicated by now, like sourcing or substituting. */ #if ENABLE_HUSH_INTERACTIVE if (interactive_fd && i->__promptme && i->file == stdin) { - while (!i->p || !(interactive_fd && i->p[0])) { - if (get_user_input(i) < 0) - return EOF; - } + do { + get_user_input(i); + } while (!*i->p); /* need non-empty line */ i->promptmode = 2; i->__promptme = 0; - if (i->p && *i->p) { - ch = *i->p++; - } + goto take_cached; } else #endif { ch = fgetc(i->file); } - debug_printf("file_get: got a %d\n", ch); } + debug_printf("file_get: got a '%c' %d\n", ch, ch); #if ENABLE_HUSH_INTERACTIVE if (ch == '\n') i->__promptme = 1; @@ -1152,12 +1154,17 @@ static int file_get(struct in_str *i) static int file_peek(struct in_str *i) { int ch; - if (i->cached_ch) { - return i->cached_ch ^ 0x100; + if (i->p && *i->p) { + if (i->eof_flag && !i->p[1]) + return EOF; + return *i->p; } ch = fgetc(i->file); - i->cached_ch = ch ^ 0x100; /* ^ 0x100 so that it is never 0 */ - debug_printf("file_peek: got a %d '%c'\n", ch, ch); + i->eof_flag = (ch == EOF); + i->peek_buf[0] = ch; + i->peek_buf[1] = '\0'; + i->p = i->peek_buf; + debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p); return ch; } @@ -1182,6 +1189,7 @@ static void setup_string_in_str(struct in_str *i, const char *s) i->promptmode = 1; #endif i->p = s; + i->eof_flag = 0; } static void mark_open(int fd) @@ -2846,11 +2854,12 @@ static FILE *generate_stream_from_list(struct pipe *head) static int process_command_subs(o_string *dest, struct p_context *ctx, struct in_str *input, const char *subst_end) { - int retcode; + int retcode, ch, eol_cnt; o_string result = NULL_O_STRING; struct p_context inner; FILE *p; struct in_str pipe_str; + initialize_context(&inner); /* recursion to generate command */ @@ -2863,25 +2872,20 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, p = generate_stream_from_list(inner.list_head); if (p == NULL) return 1; mark_open(fileno(p)); -// FIXME: need to flag pipe_str to somehow discard all trailing newlines. -// Example: echo "TEST`date;echo;echo`BEST" -// must produce one line: TEST<date>BEST setup_file_in_str(&pipe_str, p); /* now send results of command back into original context */ -// FIXME: must not do quote parsing of the output! -// Example: echo "TEST`echo '$(echo ZZ)'`BEST" -// must produce TEST$(echo ZZ)BEST, not TESTZZBEST. -// Example: echo "TEST`echo "'"`BEST" -// must produce TEST'BEST -// (maybe by setting all chars flagged as literals in map[]?) - - retcode = parse_stream(dest, ctx, &pipe_str, NULL); - /* XXX In case of a syntax error, should we try to kill the child? - * That would be tough to do right, so just read until EOF. */ - if (retcode == 1) { - while (b_getch(&pipe_str) != EOF) - /* discard */; + eol_cnt = 0; + while ((ch = b_getch(&pipe_str)) != EOF) { + if (ch == '\n') { + eol_cnt++; + continue; + } + while (eol_cnt) { + b_addqchr(dest, '\n', dest->quote); + eol_cnt--; + } + b_addqchr(dest, ch, dest->quote); } debug_printf("done reading from pipe, pclose()ing\n"); |