diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-10 23:06:55 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-05-10 23:06:55 +0000 |
commit | e0a336747c2061d0d555c4e15287b513831d2947 (patch) | |
tree | d51595efe1d14bc4f236d70a5ce7fac0a47e99cc /shell/hush.c | |
parent | 53079d494ef0c1fec481b062b3614c45e60eb057 (diff) | |
download | busybox-w32-e0a336747c2061d0d555c4e15287b513831d2947.tar.gz busybox-w32-e0a336747c2061d0d555c4e15287b513831d2947.tar.bz2 busybox-w32-e0a336747c2061d0d555c4e15287b513831d2947.zip |
hush: fix "unterminated last line loops forever" bug
hush: add testsuite infrastructure
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 129 |
1 files changed, 70 insertions, 59 deletions
diff --git a/shell/hush.c b/shell/hush.c index 8ffb117de..32cd65c72 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -317,8 +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 | const char *p; | 320 | union { |
321 | char peek_buf[2]; | 321 | const char *p; |
322 | int cached_ch; | ||
323 | }; | ||
322 | #if ENABLE_HUSH_INTERACTIVE | 324 | #if ENABLE_HUSH_INTERACTIVE |
323 | int __promptme; | 325 | int __promptme; |
324 | int promptmode; | 326 | int promptmode; |
@@ -1112,8 +1114,10 @@ static int file_get(struct in_str *i) | |||
1112 | 1114 | ||
1113 | ch = 0; | 1115 | ch = 0; |
1114 | /* If there is data waiting, eat it up */ | 1116 | /* If there is data waiting, eat it up */ |
1115 | if (i->p && *i->p) { | 1117 | if (i->cached_ch) { |
1116 | ch = *i->p++; | 1118 | ch = i->cached_ch ^ 0x100; |
1119 | if (ch != EOF) | ||
1120 | i->cached_ch = 0; | ||
1117 | } else { | 1121 | } else { |
1118 | /* need to double check i->file because we might be doing something | 1122 | /* need to double check i->file because we might be doing something |
1119 | * more complicated by now, like sourcing or substituting. */ | 1123 | * more complicated by now, like sourcing or substituting. */ |
@@ -1133,7 +1137,7 @@ static int file_get(struct in_str *i) | |||
1133 | { | 1137 | { |
1134 | ch = fgetc(i->file); | 1138 | ch = fgetc(i->file); |
1135 | } | 1139 | } |
1136 | debug_printf("b_getch: got a %d\n", ch); | 1140 | debug_printf("file_get: got a %d\n", ch); |
1137 | } | 1141 | } |
1138 | #if ENABLE_HUSH_INTERACTIVE | 1142 | #if ENABLE_HUSH_INTERACTIVE |
1139 | if (ch == '\n') | 1143 | if (ch == '\n') |
@@ -1147,14 +1151,14 @@ static int file_get(struct in_str *i) | |||
1147 | */ | 1151 | */ |
1148 | static int file_peek(struct in_str *i) | 1152 | static int file_peek(struct in_str *i) |
1149 | { | 1153 | { |
1150 | if (i->p && *i->p) { | 1154 | int ch; |
1151 | return *i->p; | 1155 | if (i->cached_ch) { |
1156 | return i->cached_ch ^ 0x100; | ||
1152 | } | 1157 | } |
1153 | i->peek_buf[0] = fgetc(i->file); | 1158 | ch = fgetc(i->file); |
1154 | i->peek_buf[1] = '\0'; | 1159 | i->cached_ch = ch ^ 0x100; /* ^ 0x100 so that it is never 0 */ |
1155 | i->p = i->peek_buf; | 1160 | debug_printf("file_peek: got a %d '%c'\n", ch, ch); |
1156 | debug_printf("b_peek: got a %d\n", *i->p); | 1161 | return ch; |
1157 | return *i->p; | ||
1158 | } | 1162 | } |
1159 | 1163 | ||
1160 | static void setup_file_in_str(struct in_str *i, FILE *f) | 1164 | static void setup_file_in_str(struct in_str *i, FILE *f) |
@@ -1670,10 +1674,10 @@ static int run_pipe_real(struct pipe *pi) | |||
1670 | int export_me = 0; | 1674 | int export_me = 0; |
1671 | char *name, *value; | 1675 | char *name, *value; |
1672 | name = xstrdup(argv[i]); | 1676 | name = xstrdup(argv[i]); |
1673 | debug_printf("Local environment set: %s\n", name); | 1677 | debug_printf("local environment set: %s\n", name); |
1674 | value = strchr(name, '='); | 1678 | value = strchr(name, '='); |
1675 | if (value) | 1679 | if (value) |
1676 | *value = 0; | 1680 | *value = '\0'; |
1677 | if (get_local_var(name)) { | 1681 | if (get_local_var(name)) { |
1678 | export_me = 1; | 1682 | export_me = 1; |
1679 | } | 1683 | } |
@@ -2364,9 +2368,10 @@ static const char *get_local_var(const char *s) | |||
2364 | 2368 | ||
2365 | if (!s) | 2369 | if (!s) |
2366 | return NULL; | 2370 | return NULL; |
2367 | for (cur = top_vars; cur; cur = cur->next) | 2371 | for (cur = top_vars; cur; cur = cur->next) { |
2368 | if (strcmp(cur->name, s) == 0) | 2372 | if (strcmp(cur->name, s) == 0) |
2369 | return cur->value; | 2373 | return cur->value; |
2374 | } | ||
2370 | return NULL; | 2375 | return NULL; |
2371 | } | 2376 | } |
2372 | 2377 | ||
@@ -2380,7 +2385,7 @@ static int set_local_var(const char *s, int flg_export) | |||
2380 | int result = 0; | 2385 | int result = 0; |
2381 | struct variables *cur; | 2386 | struct variables *cur; |
2382 | 2387 | ||
2383 | name = strdup(s); | 2388 | name = xstrdup(s); |
2384 | 2389 | ||
2385 | /* Assume when we enter this function that we are already in | 2390 | /* Assume when we enter this function that we are already in |
2386 | * NAME=VALUE format. So the first order of business is to | 2391 | * NAME=VALUE format. So the first order of business is to |
@@ -2394,48 +2399,46 @@ static int set_local_var(const char *s, int flg_export) | |||
2394 | *value++ = '\0'; | 2399 | *value++ = '\0'; |
2395 | 2400 | ||
2396 | for (cur = top_vars; cur; cur = cur->next) { | 2401 | for (cur = top_vars; cur; cur = cur->next) { |
2397 | if (strcmp(cur->name, name) == 0) | 2402 | if (strcmp(cur->name, name) == 0) { |
2398 | break; | 2403 | if (strcmp(cur->value, value) == 0) { |
2399 | } | 2404 | if (flg_export > 0 && cur->flg_export == 0) |
2400 | 2405 | cur->flg_export = flg_export; | |
2401 | if (cur) { | 2406 | else |
2402 | if (strcmp(cur->value, value) == 0) { | 2407 | result++; |
2403 | if (flg_export > 0 && cur->flg_export == 0) | 2408 | } else if (cur->flg_read_only) { |
2404 | cur->flg_export = flg_export; | 2409 | bb_error_msg("%s: readonly variable", name); |
2405 | else | ||
2406 | result++; | ||
2407 | } else if (cur->flg_read_only) { | ||
2408 | bb_error_msg("%s: readonly variable", name); | ||
2409 | result = -1; | ||
2410 | } else { | ||
2411 | if (flg_export > 0 || cur->flg_export > 1) | ||
2412 | cur->flg_export = 1; | ||
2413 | free((char*)cur->value); | ||
2414 | |||
2415 | cur->value = strdup(value); | ||
2416 | } | ||
2417 | } else { | ||
2418 | cur = malloc(sizeof(struct variables)); | ||
2419 | if (!cur) { | ||
2420 | result = -1; | ||
2421 | } else { | ||
2422 | cur->name = strdup(name); | ||
2423 | if (!cur->name) { | ||
2424 | free(cur); | ||
2425 | result = -1; | 2410 | result = -1; |
2426 | } else { | 2411 | } else { |
2427 | struct variables *bottom = top_vars; | 2412 | if (flg_export > 0 || cur->flg_export > 1) |
2413 | cur->flg_export = 1; | ||
2414 | free((char*)cur->value); | ||
2428 | cur->value = strdup(value); | 2415 | cur->value = strdup(value); |
2429 | cur->next = 0; | ||
2430 | cur->flg_export = flg_export; | ||
2431 | cur->flg_read_only = 0; | ||
2432 | while (bottom->next) | ||
2433 | bottom = bottom->next; | ||
2434 | bottom->next = cur; | ||
2435 | } | 2416 | } |
2436 | } | 2417 | } |
2418 | goto skip; | ||
2437 | } | 2419 | } |
2438 | 2420 | ||
2421 | // TODO: need simpler/generic rollback on malloc failure - see ash | ||
2422 | cur = malloc(sizeof(*cur)); | ||
2423 | if (!cur) { | ||
2424 | result = -1; | ||
2425 | } else { | ||
2426 | cur->name = strdup(name); | ||
2427 | if (!cur->name) { | ||
2428 | free(cur); | ||
2429 | result = -1; | ||
2430 | } else { | ||
2431 | struct variables *bottom = top_vars; | ||
2432 | cur->value = strdup(value); | ||
2433 | cur->next = 0; | ||
2434 | cur->flg_export = flg_export; | ||
2435 | cur->flg_read_only = 0; | ||
2436 | while (bottom->next) | ||
2437 | bottom = bottom->next; | ||
2438 | bottom->next = cur; | ||
2439 | } | ||
2440 | } | ||
2441 | skip: | ||
2439 | if (result == 0 && cur->flg_export == 1) { | 2442 | if (result == 0 && cur->flg_export == 1) { |
2440 | *(value-1) = '='; | 2443 | *(value-1) = '='; |
2441 | result = putenv(name); | 2444 | result = putenv(name); |
@@ -2975,11 +2978,16 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i | |||
2975 | int i, advance = 0; | 2978 | int i, advance = 0; |
2976 | char sep[] = " "; | 2979 | char sep[] = " "; |
2977 | int ch = input->peek(input); /* first character after the $ */ | 2980 | int ch = input->peek(input); /* first character after the $ */ |
2978 | debug_printf("handle_dollar: ch=%c\n", ch); | 2981 | |
2982 | debug_printf_parse("handle_dollar entered: ch='%c'\n", ch); | ||
2979 | if (isalpha(ch)) { | 2983 | if (isalpha(ch)) { |
2980 | b_addchr(dest, SPECIAL_VAR_SYMBOL); | 2984 | b_addchr(dest, SPECIAL_VAR_SYMBOL); |
2981 | ctx->child->sp++; | 2985 | ctx->child->sp++; |
2982 | while (ch = b_peek(input), isalnum(ch) || ch == '_') { | 2986 | while (1) { |
2987 | ch = b_peek(input); | ||
2988 | if (!isalnum(ch) && ch != '_') | ||
2989 | break; | ||
2990 | debug_printf_parse(": '%c'\n", ch); | ||
2983 | b_getch(input); | 2991 | b_getch(input); |
2984 | b_addchr(dest, ch); | 2992 | b_addchr(dest, ch); |
2985 | } | 2993 | } |
@@ -3014,14 +3022,16 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i | |||
3014 | /* XXX maybe someone will try to escape the '}' */ | 3022 | /* XXX maybe someone will try to escape the '}' */ |
3015 | while (1) { | 3023 | while (1) { |
3016 | ch = b_getch(input); | 3024 | ch = b_getch(input); |
3017 | if (ch == EOF || ch == '}') | 3025 | if (ch == EOF) { |
3026 | syntax(); | ||
3027 | debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); | ||
3028 | return 1; | ||
3029 | } | ||
3030 | if (ch == '}') | ||
3018 | break; | 3031 | break; |
3032 | debug_printf_parse(": '%c'\n", ch); | ||
3019 | b_addchr(dest, ch); | 3033 | b_addchr(dest, ch); |
3020 | } | 3034 | } |
3021 | if (ch != '}') { | ||
3022 | syntax(); | ||
3023 | return 1; | ||
3024 | } | ||
3025 | b_addchr(dest, SPECIAL_VAR_SYMBOL); | 3035 | b_addchr(dest, SPECIAL_VAR_SYMBOL); |
3026 | break; | 3036 | break; |
3027 | case '(': | 3037 | case '(': |
@@ -3052,6 +3062,7 @@ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *i | |||
3052 | * a nice size-optimized program. Hah! That'll be the day. | 3062 | * a nice size-optimized program. Hah! That'll be the day. |
3053 | */ | 3063 | */ |
3054 | if (advance) b_getch(input); | 3064 | if (advance) b_getch(input); |
3065 | debug_printf_parse("handle_dollar return 0\n"); | ||
3055 | return 0; | 3066 | return 0; |
3056 | } | 3067 | } |
3057 | 3068 | ||
@@ -3079,7 +3090,7 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3079 | 3090 | ||
3080 | while ((ch = b_getch(input)) != EOF) { | 3091 | while ((ch = b_getch(input)) != EOF) { |
3081 | m = map[ch]; | 3092 | m = map[ch]; |
3082 | next = (ch == '\n') ? 0 : b_peek(input); | 3093 | next = (ch == '\n') ? '\0' : b_peek(input); |
3083 | debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n", | 3094 | debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n", |
3084 | ch, ch, m, dest->quote); | 3095 | ch, ch, m, dest->quote); |
3085 | if (m == MAP_ORDINARY | 3096 | if (m == MAP_ORDINARY |