summaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-05-10 23:06:55 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-05-10 23:06:55 +0000
commite0a336747c2061d0d555c4e15287b513831d2947 (patch)
treed51595efe1d14bc4f236d70a5ce7fac0a47e99cc /shell/hush.c
parent53079d494ef0c1fec481b062b3614c45e60eb057 (diff)
downloadbusybox-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.c129
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? */
319struct in_str { 319struct 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 */
1148static int file_peek(struct in_str *i) 1152static 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
1160static void setup_file_in_str(struct in_str *i, FILE *f) 1164static 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