| @@ -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 ([email protected]) |
| */ |
| 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"); |