diff options
| author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-11 12:56:43 +0000 |
|---|---|---|
| committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-11 12:56:43 +0000 |
| commit | 3e9aaae5dc384ae070c49507a92b1375397954cd (patch) | |
| tree | 048661e5d8448c8cb39a4453fe36f4e6c8746384 /shell | |
| parent | e0a336747c2061d0d555c4e15287b513831d2947 (diff) | |
| download | busybox-w32-3e9aaae5dc384ae070c49507a92b1375397954cd.tar.gz busybox-w32-3e9aaae5dc384ae070c49507a92b1375397954cd.tar.bz2 busybox-w32-3e9aaae5dc384ae070c49507a92b1375397954cd.zip | |
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')
| -rw-r--r-- | shell/hush.c | 88 | ||||
| -rw-r--r-- | shell/hush_test/hush-parsing/noeol.right | 1 | ||||
| -rw-r--r-- | shell/hush_test/hush-parsing/noeol.tests | 2 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var.right | 4 | ||||
| -rw-r--r-- | shell/hush_test/hush-vars/var.tests | 10 | ||||
| -rw-r--r-- | shell/hush_test/run-all | 59 |
6 files changed, 46 insertions, 118 deletions
diff --git a/shell/hush.c b/shell/hush.c index 32cd65c72..04fb00dc7 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -317,10 +317,10 @@ typedef struct { | |||
| 317 | /* I can almost use ordinary FILE *. Is open_memstream() universally | 317 | /* I can almost use ordinary FILE *. Is open_memstream() universally |
| 318 | * available? Where is it documented? */ | 318 | * available? Where is it documented? */ |
| 319 | struct in_str { | 319 | struct in_str { |
| 320 | union { | 320 | const char *p; |
| 321 | const char *p; | 321 | /* eof_flag=1: last char in ->p is really an EOF */ |
| 322 | int cached_ch; | 322 | char eof_flag; /* meaningless if ->p == NULL */ |
| 323 | }; | 323 | char peek_buf[2]; |
| 324 | #if ENABLE_HUSH_INTERACTIVE | 324 | #if ENABLE_HUSH_INTERACTIVE |
| 325 | int __promptme; | 325 | int __promptme; |
| 326 | int promptmode; | 326 | int promptmode; |
| @@ -976,7 +976,7 @@ static int b_check_space(o_string *o, int len) | |||
| 976 | 976 | ||
| 977 | static int b_addchr(o_string *o, int ch) | 977 | static int b_addchr(o_string *o, int ch) |
| 978 | { | 978 | { |
| 979 | debug_printf("b_addchr: %c %d %p\n", ch, o->length, o); | 979 | debug_printf("b_addchr: '%c' o->lengtt=%d o=%p\n", ch, o->length, o); |
| 980 | if (b_check_space(o, 1)) | 980 | if (b_check_space(o, 1)) |
| 981 | return B_NOSPAC; | 981 | return B_NOSPAC; |
| 982 | o->data[o->length] = ch; | 982 | o->data[o->length] = ch; |
| @@ -1079,12 +1079,13 @@ static const char* setup_prompt_string(int promptmode) | |||
| 1079 | static line_input_t *line_input_state; | 1079 | static line_input_t *line_input_state; |
| 1080 | #endif | 1080 | #endif |
| 1081 | 1081 | ||
| 1082 | static int get_user_input(struct in_str *i) | 1082 | static void get_user_input(struct in_str *i) |
| 1083 | { | 1083 | { |
| 1084 | static char the_command[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; | 1084 | static char the_command[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; |
| 1085 | 1085 | ||
| 1086 | int r; | 1086 | int r; |
| 1087 | const char *prompt_str; | 1087 | const char *prompt_str; |
| 1088 | |||
| 1088 | prompt_str = setup_prompt_string(i->promptmode); | 1089 | prompt_str = setup_prompt_string(i->promptmode); |
| 1089 | #if ENABLE_FEATURE_EDITING | 1090 | #if ENABLE_FEATURE_EDITING |
| 1090 | /* | 1091 | /* |
| @@ -1094,15 +1095,19 @@ static int get_user_input(struct in_str *i) | |||
| 1094 | ** child processes (rob@sysgo.de) | 1095 | ** child processes (rob@sysgo.de) |
| 1095 | */ | 1096 | */ |
| 1096 | r = read_line_input(prompt_str, the_command, BUFSIZ-1, line_input_state); | 1097 | r = read_line_input(prompt_str, the_command, BUFSIZ-1, line_input_state); |
| 1098 | i->eof_flag = (r < 0); | ||
| 1099 | if (i->eof_flag) { /* EOF/error detected */ | ||
| 1100 | the_command[0] = EOF; /* yes, it will be truncated, it's ok */ | ||
| 1101 | the_command[1] = '\0'; | ||
| 1102 | } | ||
| 1097 | #else | 1103 | #else |
| 1098 | fputs(prompt_str, stdout); | 1104 | fputs(prompt_str, stdout); |
| 1099 | fflush(stdout); | 1105 | fflush(stdout); |
| 1100 | the_command[0] = r = fgetc(i->file); | 1106 | the_command[0] = r = fgetc(i->file); |
| 1101 | /*the_command[1] = '\0'; - already is and never changed */ | 1107 | /*the_command[1] = '\0'; - already is and never changed */ |
| 1108 | i->eof_flag = (r == EOF); | ||
| 1102 | #endif | 1109 | #endif |
| 1103 | fflush(stdout); | ||
| 1104 | i->p = the_command; | 1110 | i->p = the_command; |
| 1105 | return r; /* < 0 == EOF. Not meaningful otherwise */ | ||
| 1106 | } | 1111 | } |
| 1107 | #endif /* INTERACTIVE */ | 1112 | #endif /* INTERACTIVE */ |
| 1108 | 1113 | ||
| @@ -1112,33 +1117,30 @@ static int file_get(struct in_str *i) | |||
| 1112 | { | 1117 | { |
| 1113 | int ch; | 1118 | int ch; |
| 1114 | 1119 | ||
| 1115 | ch = 0; | ||
| 1116 | /* If there is data waiting, eat it up */ | 1120 | /* If there is data waiting, eat it up */ |
| 1117 | if (i->cached_ch) { | 1121 | if (i->p && *i->p) { |
| 1118 | ch = i->cached_ch ^ 0x100; | 1122 | take_cached: |
| 1119 | if (ch != EOF) | 1123 | ch = *i->p++; |
| 1120 | i->cached_ch = 0; | 1124 | if (i->eof_flag && !*i->p) |
| 1125 | ch = EOF; | ||
| 1121 | } else { | 1126 | } else { |
| 1122 | /* need to double check i->file because we might be doing something | 1127 | /* need to double check i->file because we might be doing something |
| 1123 | * more complicated by now, like sourcing or substituting. */ | 1128 | * more complicated by now, like sourcing or substituting. */ |
| 1124 | #if ENABLE_HUSH_INTERACTIVE | 1129 | #if ENABLE_HUSH_INTERACTIVE |
| 1125 | if (interactive_fd && i->__promptme && i->file == stdin) { | 1130 | if (interactive_fd && i->__promptme && i->file == stdin) { |
| 1126 | while (!i->p || !(interactive_fd && i->p[0])) { | 1131 | do { |
| 1127 | if (get_user_input(i) < 0) | 1132 | get_user_input(i); |
| 1128 | return EOF; | 1133 | } while (!*i->p); /* need non-empty line */ |
| 1129 | } | ||
| 1130 | i->promptmode = 2; | 1134 | i->promptmode = 2; |
| 1131 | i->__promptme = 0; | 1135 | i->__promptme = 0; |
| 1132 | if (i->p && *i->p) { | 1136 | goto take_cached; |
| 1133 | ch = *i->p++; | ||
| 1134 | } | ||
| 1135 | } else | 1137 | } else |
| 1136 | #endif | 1138 | #endif |
| 1137 | { | 1139 | { |
| 1138 | ch = fgetc(i->file); | 1140 | ch = fgetc(i->file); |
| 1139 | } | 1141 | } |
| 1140 | debug_printf("file_get: got a %d\n", ch); | ||
| 1141 | } | 1142 | } |
| 1143 | debug_printf("file_get: got a '%c' %d\n", ch, ch); | ||
| 1142 | #if ENABLE_HUSH_INTERACTIVE | 1144 | #if ENABLE_HUSH_INTERACTIVE |
| 1143 | if (ch == '\n') | 1145 | if (ch == '\n') |
| 1144 | i->__promptme = 1; | 1146 | i->__promptme = 1; |
| @@ -1152,12 +1154,17 @@ static int file_get(struct in_str *i) | |||
| 1152 | static int file_peek(struct in_str *i) | 1154 | static int file_peek(struct in_str *i) |
| 1153 | { | 1155 | { |
| 1154 | int ch; | 1156 | int ch; |
| 1155 | if (i->cached_ch) { | 1157 | if (i->p && *i->p) { |
| 1156 | return i->cached_ch ^ 0x100; | 1158 | if (i->eof_flag && !i->p[1]) |
| 1159 | return EOF; | ||
| 1160 | return *i->p; | ||
| 1157 | } | 1161 | } |
| 1158 | ch = fgetc(i->file); | 1162 | ch = fgetc(i->file); |
| 1159 | i->cached_ch = ch ^ 0x100; /* ^ 0x100 so that it is never 0 */ | 1163 | i->eof_flag = (ch == EOF); |
| 1160 | debug_printf("file_peek: got a %d '%c'\n", ch, ch); | 1164 | i->peek_buf[0] = ch; |
| 1165 | i->peek_buf[1] = '\0'; | ||
| 1166 | i->p = i->peek_buf; | ||
| 1167 | debug_printf("file_peek: got a '%c' %d\n", *i->p, *i->p); | ||
| 1161 | return ch; | 1168 | return ch; |
| 1162 | } | 1169 | } |
| 1163 | 1170 | ||
| @@ -1182,6 +1189,7 @@ static void setup_string_in_str(struct in_str *i, const char *s) | |||
| 1182 | i->promptmode = 1; | 1189 | i->promptmode = 1; |
| 1183 | #endif | 1190 | #endif |
| 1184 | i->p = s; | 1191 | i->p = s; |
| 1192 | i->eof_flag = 0; | ||
| 1185 | } | 1193 | } |
| 1186 | 1194 | ||
| 1187 | static void mark_open(int fd) | 1195 | static void mark_open(int fd) |
| @@ -2846,11 +2854,12 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
| 2846 | static int process_command_subs(o_string *dest, struct p_context *ctx, | 2854 | static int process_command_subs(o_string *dest, struct p_context *ctx, |
| 2847 | struct in_str *input, const char *subst_end) | 2855 | struct in_str *input, const char *subst_end) |
| 2848 | { | 2856 | { |
| 2849 | int retcode; | 2857 | int retcode, ch, eol_cnt; |
| 2850 | o_string result = NULL_O_STRING; | 2858 | o_string result = NULL_O_STRING; |
| 2851 | struct p_context inner; | 2859 | struct p_context inner; |
| 2852 | FILE *p; | 2860 | FILE *p; |
| 2853 | struct in_str pipe_str; | 2861 | struct in_str pipe_str; |
| 2862 | |||
| 2854 | initialize_context(&inner); | 2863 | initialize_context(&inner); |
| 2855 | 2864 | ||
| 2856 | /* recursion to generate command */ | 2865 | /* recursion to generate command */ |
| @@ -2863,25 +2872,20 @@ static int process_command_subs(o_string *dest, struct p_context *ctx, | |||
| 2863 | p = generate_stream_from_list(inner.list_head); | 2872 | p = generate_stream_from_list(inner.list_head); |
| 2864 | if (p == NULL) return 1; | 2873 | if (p == NULL) return 1; |
| 2865 | mark_open(fileno(p)); | 2874 | mark_open(fileno(p)); |
| 2866 | // FIXME: need to flag pipe_str to somehow discard all trailing newlines. | ||
| 2867 | // Example: echo "TEST`date;echo;echo`BEST" | ||
| 2868 | // must produce one line: TEST<date>BEST | ||
| 2869 | setup_file_in_str(&pipe_str, p); | 2875 | setup_file_in_str(&pipe_str, p); |
| 2870 | 2876 | ||
| 2871 | /* now send results of command back into original context */ | 2877 | /* now send results of command back into original context */ |
| 2872 | // FIXME: must not do quote parsing of the output! | 2878 | eol_cnt = 0; |
| 2873 | // Example: echo "TEST`echo '$(echo ZZ)'`BEST" | 2879 | while ((ch = b_getch(&pipe_str)) != EOF) { |
| 2874 | // must produce TEST$(echo ZZ)BEST, not TESTZZBEST. | 2880 | if (ch == '\n') { |
| 2875 | // Example: echo "TEST`echo "'"`BEST" | 2881 | eol_cnt++; |
| 2876 | // must produce TEST'BEST | 2882 | continue; |
| 2877 | // (maybe by setting all chars flagged as literals in map[]?) | 2883 | } |
| 2878 | 2884 | while (eol_cnt) { | |
| 2879 | retcode = parse_stream(dest, ctx, &pipe_str, NULL); | 2885 | b_addqchr(dest, '\n', dest->quote); |
| 2880 | /* XXX In case of a syntax error, should we try to kill the child? | 2886 | eol_cnt--; |
| 2881 | * That would be tough to do right, so just read until EOF. */ | 2887 | } |
| 2882 | if (retcode == 1) { | 2888 | b_addqchr(dest, ch, dest->quote); |
| 2883 | while (b_getch(&pipe_str) != EOF) | ||
| 2884 | /* discard */; | ||
| 2885 | } | 2889 | } |
| 2886 | 2890 | ||
| 2887 | debug_printf("done reading from pipe, pclose()ing\n"); | 2891 | debug_printf("done reading from pipe, pclose()ing\n"); |
diff --git a/shell/hush_test/hush-parsing/noeol.right b/shell/hush_test/hush-parsing/noeol.right deleted file mode 100644 index e427984d4..000000000 --- a/shell/hush_test/hush-parsing/noeol.right +++ /dev/null | |||
| @@ -1 +0,0 @@ | |||
| 1 | HELLO | ||
diff --git a/shell/hush_test/hush-parsing/noeol.tests b/shell/hush_test/hush-parsing/noeol.tests deleted file mode 100644 index a93113a03..000000000 --- a/shell/hush_test/hush-parsing/noeol.tests +++ /dev/null | |||
| @@ -1,2 +0,0 @@ | |||
| 1 | # next line has no EOL! | ||
| 2 | echo HELLO \ No newline at end of file | ||
diff --git a/shell/hush_test/hush-vars/var.right b/shell/hush_test/hush-vars/var.right deleted file mode 100644 index c13b98e38..000000000 --- a/shell/hush_test/hush-vars/var.right +++ /dev/null | |||
| @@ -1,4 +0,0 @@ | |||
| 1 | http://busybox.net | ||
| 2 | http://busybox.net_abc | ||
| 3 | 1 | ||
| 4 | 0 | ||
diff --git a/shell/hush_test/hush-vars/var.tests b/shell/hush_test/hush-vars/var.tests deleted file mode 100644 index b0637ea6b..000000000 --- a/shell/hush_test/hush-vars/var.tests +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | URL=http://busybox.net | ||
| 2 | |||
| 3 | echo $URL | ||
| 4 | echo ${URL}_abc | ||
| 5 | |||
| 6 | true | ||
| 7 | false; echo $? | ||
| 8 | true | ||
| 9 | # BUG: prints 0, must be 1 | ||
| 10 | { false; echo $?; } | ||
diff --git a/shell/hush_test/run-all b/shell/hush_test/run-all deleted file mode 100644 index 2c2bac6d2..000000000 --- a/shell/hush_test/run-all +++ /dev/null | |||
| @@ -1,59 +0,0 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | test -x hush || { echo "No ./hush?!"; exit; } | ||
| 4 | |||
| 5 | PATH="$PWD:$PATH" # for hush and recho/zecho/printenv | ||
| 6 | export PATH | ||
| 7 | |||
| 8 | THIS_SH="$PWD/hush" | ||
| 9 | export THIS_SH | ||
| 10 | |||
| 11 | do_test() | ||
| 12 | { | ||
| 13 | test -d "$1" || return 0 | ||
| 14 | ( | ||
| 15 | cd "$1" || { echo "cannot cd $1!"; exit 1; } | ||
| 16 | for x in run-*; do | ||
| 17 | test -f "$x" || continue | ||
| 18 | case "$x" in | ||
| 19 | "$0"|run-minimal|run-gprof) ;; | ||
| 20 | *.orig|*~) ;; | ||
| 21 | #*) echo $x ; sh $x ;; | ||
| 22 | *) | ||
| 23 | sh "$x" >"../$1-$x.fail" 2>&1 && \ | ||
| 24 | { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail"; | ||
| 25 | ;; | ||
| 26 | esac | ||
| 27 | done | ||
| 28 | # Many bash run-XXX scripts just do this, | ||
| 29 | # no point in duplication it all over the place | ||
| 30 | for x in *.tests; do | ||
| 31 | test -x "$x" || continue | ||
| 32 | name="${x%%.tests}" | ||
| 33 | test -f "$name.right" || continue | ||
| 34 | { | ||
| 35 | "$THIS_SH" "./$x" >"$name.xx" 2>&1 | ||
| 36 | diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail" | ||
| 37 | } && echo "$1/$x: ok" || echo "$1/$x: fail" | ||
| 38 | done | ||
| 39 | ) | ||
| 40 | } | ||
| 41 | |||
| 42 | # main part of this script | ||
| 43 | # Usage: run-all [directories] | ||
| 44 | |||
| 45 | if [ $# -lt 1 ]; then | ||
| 46 | # All sub directories | ||
| 47 | modules=`ls -d hush-*` | ||
| 48 | |||
| 49 | for module in $modules; do | ||
| 50 | do_test $module | ||
| 51 | done | ||
| 52 | else | ||
| 53 | while [ $# -ge 1 ]; do | ||
| 54 | if [ -d $1 ]; then | ||
| 55 | do_test $1 | ||
| 56 | fi | ||
| 57 | shift | ||
| 58 | done | ||
| 59 | fi | ||
