diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-06-03 12:47:26 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-06-03 12:47:26 +0200 |
commit | 295fef80bcc994bd668f369ad863ad8d12c962b7 (patch) | |
tree | ab711839e511e2bfa87263d5f06d90da03789c66 | |
parent | 98f74034cd23e708686bda38975fa0691e5be971 (diff) | |
download | busybox-w32-295fef80bcc994bd668f369ad863ad8d12c962b7.tar.gz busybox-w32-295fef80bcc994bd668f369ad863ad8d12c962b7.tar.bz2 busybox-w32-295fef80bcc994bd668f369ad863ad8d12c962b7.zip |
hush: add support for local builtin
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/Config.in | 7 | ||||
-rw-r--r-- | shell/hush.c | 244 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/func_local1.right | 3 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/func_local1.tests | 5 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/func_local2.right | 14 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/func_local2.tests | 7 |
6 files changed, 210 insertions, 70 deletions
diff --git a/shell/Config.in b/shell/Config.in index 57969f02c..ee56d89f3 100644 --- a/shell/Config.in +++ b/shell/Config.in | |||
@@ -232,6 +232,13 @@ config HUSH_FUNCTIONS | |||
232 | help | 232 | help |
233 | Enable support for shell functions in hush. +800 bytes. | 233 | Enable support for shell functions in hush. +800 bytes. |
234 | 234 | ||
235 | config HUSH_LOCAL | ||
236 | bool "Support local builtin" | ||
237 | default n | ||
238 | depends on HUSH_FUNCTIONS | ||
239 | help | ||
240 | Enable support for local variables in functions. | ||
241 | |||
235 | config HUSH_EXPORT_N | 242 | config HUSH_EXPORT_N |
236 | bool "Support export '-n' option" | 243 | bool "Support export '-n' option" |
237 | default n | 244 | default n |
diff --git a/shell/hush.c b/shell/hush.c index e3f7b6eb7..1ad5fcd72 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -394,6 +394,9 @@ struct parse_context { | |||
394 | struct variable { | 394 | struct variable { |
395 | struct variable *next; | 395 | struct variable *next; |
396 | char *varstr; /* points to "name=" portion */ | 396 | char *varstr; /* points to "name=" portion */ |
397 | #if ENABLE_HUSH_LOCAL | ||
398 | unsigned func_nest_level; | ||
399 | #endif | ||
397 | int max_len; /* if > 0, name is part of initial env; else name is malloced */ | 400 | int max_len; /* if > 0, name is part of initial env; else name is malloced */ |
398 | smallint flg_export; /* putenv should be done on this var */ | 401 | smallint flg_export; /* putenv should be done on this var */ |
399 | smallint flg_read_only; | 402 | smallint flg_read_only; |
@@ -488,6 +491,10 @@ struct globals { | |||
488 | struct variable shell_ver; | 491 | struct variable shell_ver; |
489 | #if ENABLE_HUSH_FUNCTIONS | 492 | #if ENABLE_HUSH_FUNCTIONS |
490 | struct function *top_func; | 493 | struct function *top_func; |
494 | # if ENABLE_HUSH_LOCAL | ||
495 | struct variable **shadowed_vars_pp; | ||
496 | unsigned func_nest_level; | ||
497 | # endif | ||
491 | #endif | 498 | #endif |
492 | /* Signal and trap handling */ | 499 | /* Signal and trap handling */ |
493 | #if ENABLE_HUSH_FAST | 500 | #if ENABLE_HUSH_FAST |
@@ -529,6 +536,9 @@ static int builtin_jobs(char **argv); | |||
529 | #if ENABLE_HUSH_HELP | 536 | #if ENABLE_HUSH_HELP |
530 | static int builtin_help(char **argv); | 537 | static int builtin_help(char **argv); |
531 | #endif | 538 | #endif |
539 | #if ENABLE_HUSH_LOCAL | ||
540 | static int builtin_local(char **argv); | ||
541 | #endif | ||
532 | #if HUSH_DEBUG | 542 | #if HUSH_DEBUG |
533 | static int builtin_memleak(char **argv); | 543 | static int builtin_memleak(char **argv); |
534 | #endif | 544 | #endif |
@@ -599,6 +609,9 @@ static const struct built_in_command bltins[] = { | |||
599 | #if ENABLE_HUSH_JOB | 609 | #if ENABLE_HUSH_JOB |
600 | BLTIN("jobs" , builtin_jobs , "List active jobs"), | 610 | BLTIN("jobs" , builtin_jobs , "List active jobs"), |
601 | #endif | 611 | #endif |
612 | #if ENABLE_HUSH_LOCAL | ||
613 | BLTIN("local" , builtin_local , "Set local variable"), | ||
614 | #endif | ||
602 | #if HUSH_DEBUG | 615 | #if HUSH_DEBUG |
603 | BLTIN("memleak" , builtin_memleak , "Debug tool"), | 616 | BLTIN("memleak" , builtin_memleak , "Debug tool"), |
604 | #endif | 617 | #endif |
@@ -1261,12 +1274,21 @@ static const char *get_local_var_value(const char *name) | |||
1261 | * -1: clear export flag and unsetenv the variable | 1274 | * -1: clear export flag and unsetenv the variable |
1262 | * flg_read_only is set only when we handle -R var=val | 1275 | * flg_read_only is set only when we handle -R var=val |
1263 | */ | 1276 | */ |
1264 | #if BB_MMU | 1277 | #if !BB_MMU && ENABLE_HUSH_LOCAL |
1265 | #define set_local_var(str, flg_export, flg_read_only) \ | 1278 | /* all params are used */ |
1279 | #elif BB_MMU && ENABLE_HUSH_LOCAL | ||
1280 | #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ | ||
1281 | set_local_var(str, flg_export, local_lvl) | ||
1282 | #elif BB_MMU && !ENABLE_HUSH_LOCAL | ||
1283 | #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ | ||
1266 | set_local_var(str, flg_export) | 1284 | set_local_var(str, flg_export) |
1285 | #elif !BB_MMU && !ENABLE_HUSH_LOCAL | ||
1286 | #define set_local_var(str, flg_export, local_lvl, flg_read_only) \ | ||
1287 | set_local_var(str, flg_export, flg_read_only) | ||
1267 | #endif | 1288 | #endif |
1268 | static int set_local_var(char *str, int flg_export, int flg_read_only) | 1289 | static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only) |
1269 | { | 1290 | { |
1291 | struct variable **var_pp; | ||
1270 | struct variable *cur; | 1292 | struct variable *cur; |
1271 | char *eq_sign; | 1293 | char *eq_sign; |
1272 | int name_len; | 1294 | int name_len; |
@@ -1278,15 +1300,10 @@ static int set_local_var(char *str, int flg_export, int flg_read_only) | |||
1278 | } | 1300 | } |
1279 | 1301 | ||
1280 | name_len = eq_sign - str + 1; /* including '=' */ | 1302 | name_len = eq_sign - str + 1; /* including '=' */ |
1281 | cur = G.top_var; /* cannot be NULL (we have HUSH_VERSION and it's RO) */ | 1303 | var_pp = &G.top_var; |
1282 | while (1) { | 1304 | while ((cur = *var_pp) != NULL) { |
1283 | if (strncmp(cur->varstr, str, name_len) != 0) { | 1305 | if (strncmp(cur->varstr, str, name_len) != 0) { |
1284 | if (!cur->next) { | 1306 | var_pp = &cur->next; |
1285 | /* Bail out. Note that now cur points | ||
1286 | * to the last var in the linked list */ | ||
1287 | break; | ||
1288 | } | ||
1289 | cur = cur->next; | ||
1290 | continue; | 1307 | continue; |
1291 | } | 1308 | } |
1292 | /* We found an existing var with this name */ | 1309 | /* We found an existing var with this name */ |
@@ -1298,33 +1315,61 @@ static int set_local_var(char *str, int flg_export, int flg_read_only) | |||
1298 | free(str); | 1315 | free(str); |
1299 | return -1; | 1316 | return -1; |
1300 | } | 1317 | } |
1301 | if (flg_export == -1) { | 1318 | if (flg_export == -1) { // "&& cur->flg_export" ? |
1302 | debug_printf_env("%s: unsetenv '%s'\n", __func__, str); | 1319 | debug_printf_env("%s: unsetenv '%s'\n", __func__, str); |
1303 | *eq_sign = '\0'; | 1320 | *eq_sign = '\0'; |
1304 | unsetenv(str); | 1321 | unsetenv(str); |
1305 | *eq_sign = '='; | 1322 | *eq_sign = '='; |
1306 | } | 1323 | } |
1324 | #if ENABLE_HUSH_LOCAL | ||
1325 | if (cur->func_nest_level < local_lvl) { | ||
1326 | /* New variable is declared as local, | ||
1327 | * and existing one is global, or local | ||
1328 | * from enclosing function. | ||
1329 | * Remove and save old one: */ | ||
1330 | *var_pp = cur->next; | ||
1331 | cur->next = *G.shadowed_vars_pp; | ||
1332 | *G.shadowed_vars_pp = cur; | ||
1333 | /* bash 3.2.33(1) and exported vars: | ||
1334 | * # export z=z | ||
1335 | * # f() { local z=a; env | grep ^z; } | ||
1336 | * # f | ||
1337 | * z=a | ||
1338 | * # env | grep ^z | ||
1339 | * z=z | ||
1340 | */ | ||
1341 | if (cur->flg_export) | ||
1342 | flg_export = 1; | ||
1343 | break; | ||
1344 | } | ||
1345 | #endif | ||
1307 | if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) { | 1346 | if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) { |
1308 | free_and_exp: | 1347 | free_and_exp: |
1309 | free(str); | 1348 | free(str); |
1310 | goto exp; | 1349 | goto exp; |
1311 | } | 1350 | } |
1312 | if (cur->max_len >= strlen(str)) { | 1351 | if (cur->max_len != 0) { |
1313 | /* This one is from startup env, reuse space */ | 1352 | if (cur->max_len >= strlen(str)) { |
1314 | strcpy(cur->varstr, str); | 1353 | /* This one is from startup env, reuse space */ |
1315 | goto free_and_exp; | 1354 | strcpy(cur->varstr, str); |
1316 | } | 1355 | goto free_and_exp; |
1317 | /* max_len == 0 signifies "malloced" var, which we can | 1356 | } |
1318 | * (and has to) free */ | 1357 | } else { |
1319 | if (!cur->max_len) | 1358 | /* max_len == 0 signifies "malloced" var, which we can |
1359 | * (and has to) free */ | ||
1320 | free(cur->varstr); | 1360 | free(cur->varstr); |
1361 | } | ||
1321 | cur->max_len = 0; | 1362 | cur->max_len = 0; |
1322 | goto set_str_and_exp; | 1363 | goto set_str_and_exp; |
1323 | } | 1364 | } |
1324 | 1365 | ||
1325 | /* Not found - create next variable struct */ | 1366 | /* Not found - create new variable struct */ |
1326 | cur->next = xzalloc(sizeof(*cur)); | 1367 | cur = xzalloc(sizeof(*cur)); |
1327 | cur = cur->next; | 1368 | #if ENABLE_HUSH_LOCAL |
1369 | cur->func_nest_level = local_lvl; | ||
1370 | #endif | ||
1371 | cur->next = *var_pp; | ||
1372 | *var_pp = cur; | ||
1328 | 1373 | ||
1329 | set_str_and_exp: | 1374 | set_str_and_exp: |
1330 | cur->varstr = str; | 1375 | cur->varstr = str; |
@@ -1418,7 +1463,7 @@ static void arith_set_local_var(const char *name, const char *val, int flags) | |||
1418 | { | 1463 | { |
1419 | /* arith code doesnt malloc space, so do it for it */ | 1464 | /* arith code doesnt malloc space, so do it for it */ |
1420 | char *var = xasprintf("%s=%s", name, val); | 1465 | char *var = xasprintf("%s=%s", name, val); |
1421 | set_local_var(var, flags, 0); | 1466 | set_local_var(var, flags, /*lvl:*/ 0, /*ro:*/ 0); |
1422 | } | 1467 | } |
1423 | #endif | 1468 | #endif |
1424 | 1469 | ||
@@ -1438,7 +1483,7 @@ static void add_vars(struct variable *var) | |||
1438 | debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr); | 1483 | debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr); |
1439 | putenv(var->varstr); | 1484 | putenv(var->varstr); |
1440 | } else { | 1485 | } else { |
1441 | debug_printf_env("%s: restoring local '%s'\n", __func__, var->varstr); | 1486 | debug_printf_env("%s: restoring variable '%s'\n", __func__, var->varstr); |
1442 | } | 1487 | } |
1443 | var = next; | 1488 | var = next; |
1444 | } | 1489 | } |
@@ -1471,7 +1516,7 @@ static struct variable *set_vars_and_save_old(char **strings) | |||
1471 | var_p->next = old; | 1516 | var_p->next = old; |
1472 | old = var_p; | 1517 | old = var_p; |
1473 | } | 1518 | } |
1474 | set_local_var(*s, 1, 0); | 1519 | set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0); |
1475 | } | 1520 | } |
1476 | s++; | 1521 | s++; |
1477 | } | 1522 | } |
@@ -2302,7 +2347,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2302 | val = NULL; | 2347 | val = NULL; |
2303 | } else { | 2348 | } else { |
2304 | char *new_var = xasprintf("%s=%s", var, val); | 2349 | char *new_var = xasprintf("%s=%s", var, val); |
2305 | set_local_var(new_var, 0, 0); | 2350 | set_local_var(new_var, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); |
2306 | } | 2351 | } |
2307 | } | 2352 | } |
2308 | } | 2353 | } |
@@ -3036,9 +3081,13 @@ static int run_function(const struct function *funcp, char **argv) | |||
3036 | smallint sv_flg; | 3081 | smallint sv_flg; |
3037 | 3082 | ||
3038 | save_and_replace_G_args(&sv, argv); | 3083 | save_and_replace_G_args(&sv, argv); |
3084 | |||
3039 | /* "we are in function, ok to use return" */ | 3085 | /* "we are in function, ok to use return" */ |
3040 | sv_flg = G.flag_return_in_progress; | 3086 | sv_flg = G.flag_return_in_progress; |
3041 | G.flag_return_in_progress = -1; | 3087 | G.flag_return_in_progress = -1; |
3088 | #if ENABLE_HUSH_LOCAL | ||
3089 | G.func_nest_level++; | ||
3090 | #endif | ||
3042 | 3091 | ||
3043 | /* On MMU, funcp->body is always non-NULL */ | 3092 | /* On MMU, funcp->body is always non-NULL */ |
3044 | # if !BB_MMU | 3093 | # if !BB_MMU |
@@ -3052,7 +3101,32 @@ static int run_function(const struct function *funcp, char **argv) | |||
3052 | rc = run_list(funcp->body); | 3101 | rc = run_list(funcp->body); |
3053 | } | 3102 | } |
3054 | 3103 | ||
3104 | #if ENABLE_HUSH_LOCAL | ||
3105 | { | ||
3106 | struct variable *var; | ||
3107 | struct variable **var_pp; | ||
3108 | |||
3109 | var_pp = &G.top_var; | ||
3110 | while ((var = *var_pp) != NULL) { | ||
3111 | if (var->func_nest_level < G.func_nest_level) { | ||
3112 | var_pp = &var->next; | ||
3113 | continue; | ||
3114 | } | ||
3115 | /* Unexport */ | ||
3116 | if (var->flg_export) | ||
3117 | bb_unsetenv(var->varstr); | ||
3118 | /* Remove from global list */ | ||
3119 | *var_pp = var->next; | ||
3120 | /* Free */ | ||
3121 | if (!var->max_len) | ||
3122 | free(var->varstr); | ||
3123 | free(var); | ||
3124 | } | ||
3125 | G.func_nest_level--; | ||
3126 | } | ||
3127 | #endif | ||
3055 | G.flag_return_in_progress = sv_flg; | 3128 | G.flag_return_in_progress = sv_flg; |
3129 | |||
3056 | restore_G_args(&sv, argv); | 3130 | restore_G_args(&sv, argv); |
3057 | 3131 | ||
3058 | return rc; | 3132 | return rc; |
@@ -3606,7 +3680,7 @@ static int run_pipe(struct pipe *pi) | |||
3606 | p = expand_string_to_string(*argv); | 3680 | p = expand_string_to_string(*argv); |
3607 | debug_printf_exec("set shell var:'%s'->'%s'\n", | 3681 | debug_printf_exec("set shell var:'%s'->'%s'\n", |
3608 | *argv, p); | 3682 | *argv, p); |
3609 | set_local_var(p, 0, 0); | 3683 | set_local_var(p, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); |
3610 | argv++; | 3684 | argv++; |
3611 | } | 3685 | } |
3612 | /* Do we need to flag set_local_var() errors? | 3686 | /* Do we need to flag set_local_var() errors? |
@@ -3651,9 +3725,17 @@ static int run_pipe(struct pipe *pi) | |||
3651 | } | 3725 | } |
3652 | #if ENABLE_HUSH_FUNCTIONS | 3726 | #if ENABLE_HUSH_FUNCTIONS |
3653 | else { | 3727 | else { |
3728 | # if ENABLE_HUSH_LOCAL | ||
3729 | struct variable **sv; | ||
3730 | sv = G.shadowed_vars_pp; | ||
3731 | G.shadowed_vars_pp = &old_vars; | ||
3732 | # endif | ||
3654 | debug_printf_exec(": function '%s' '%s'...\n", | 3733 | debug_printf_exec(": function '%s' '%s'...\n", |
3655 | funcp->name, argv_expanded[1]); | 3734 | funcp->name, argv_expanded[1]); |
3656 | rcode = run_function(funcp, argv_expanded) & 0xff; | 3735 | rcode = run_function(funcp, argv_expanded) & 0xff; |
3736 | # if ENABLE_HUSH_LOCAL | ||
3737 | G.shadowed_vars_pp = sv; | ||
3738 | # endif | ||
3657 | } | 3739 | } |
3658 | #endif | 3740 | #endif |
3659 | } | 3741 | } |
@@ -4050,7 +4132,7 @@ static int run_list(struct pipe *pi) | |||
4050 | } | 4132 | } |
4051 | /* Insert next value from for_lcur */ | 4133 | /* Insert next value from for_lcur */ |
4052 | /* note: *for_lcur already has quotes removed, $var expanded, etc */ | 4134 | /* note: *for_lcur already has quotes removed, $var expanded, etc */ |
4053 | set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), 0, 0); | 4135 | set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); |
4054 | continue; | 4136 | continue; |
4055 | } | 4137 | } |
4056 | if (rword == RES_IN) { | 4138 | if (rword == RES_IN) { |
@@ -6250,7 +6332,7 @@ int hush_main(int argc, char **argv) | |||
6250 | break; | 6332 | break; |
6251 | case 'R': | 6333 | case 'R': |
6252 | case 'V': | 6334 | case 'V': |
6253 | set_local_var(xstrdup(optarg), 0, opt == 'R'); | 6335 | set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R'); |
6254 | break; | 6336 | break; |
6255 | # if ENABLE_HUSH_FUNCTIONS | 6337 | # if ENABLE_HUSH_FUNCTIONS |
6256 | case 'F': { | 6338 | case 'F': { |
@@ -6583,6 +6665,55 @@ static void print_escaped(const char *s) | |||
6583 | } while (*s); | 6665 | } while (*s); |
6584 | } | 6666 | } |
6585 | 6667 | ||
6668 | #if !ENABLE_HUSH_LOCAL | ||
6669 | #define helper_export_local(argv, exp, lvl) \ | ||
6670 | helper_export_local(argv, exp) | ||
6671 | #endif | ||
6672 | static void helper_export_local(char **argv, int exp, int lvl) | ||
6673 | { | ||
6674 | do { | ||
6675 | char *name = *argv; | ||
6676 | |||
6677 | /* So far we do not check that name is valid (TODO?) */ | ||
6678 | |||
6679 | if (strchr(name, '=') == NULL) { | ||
6680 | struct variable *var; | ||
6681 | |||
6682 | var = get_local_var(name); | ||
6683 | if (exp == -1) { /* unexporting? */ | ||
6684 | /* export -n NAME (without =VALUE) */ | ||
6685 | if (var) { | ||
6686 | var->flg_export = 0; | ||
6687 | debug_printf_env("%s: unsetenv '%s'\n", __func__, name); | ||
6688 | unsetenv(name); | ||
6689 | } /* else: export -n NOT_EXISTING_VAR: no-op */ | ||
6690 | continue; | ||
6691 | } | ||
6692 | if (exp == 1) { /* exporting? */ | ||
6693 | /* export NAME (without =VALUE) */ | ||
6694 | if (var) { | ||
6695 | var->flg_export = 1; | ||
6696 | debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr); | ||
6697 | putenv(var->varstr); | ||
6698 | continue; | ||
6699 | } | ||
6700 | } | ||
6701 | /* Exporting non-existing variable. | ||
6702 | * bash does not put it in environment, | ||
6703 | * but remembers that it is exported, | ||
6704 | * and does put it in env when it is set later. | ||
6705 | * We just set it to "" and export. */ | ||
6706 | /* Or, it's "local NAME" (without =VALUE). | ||
6707 | * bash sets the value to "". */ | ||
6708 | name = xasprintf("%s=", name); | ||
6709 | } else { | ||
6710 | /* (Un)exporting/making local NAME=VALUE */ | ||
6711 | name = xstrdup(name); | ||
6712 | } | ||
6713 | set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); | ||
6714 | } while (*++argv); | ||
6715 | } | ||
6716 | |||
6586 | static int builtin_export(char **argv) | 6717 | static int builtin_export(char **argv) |
6587 | { | 6718 | { |
6588 | unsigned opt_unexport; | 6719 | unsigned opt_unexport; |
@@ -6625,49 +6756,22 @@ static int builtin_export(char **argv) | |||
6625 | argv++; | 6756 | argv++; |
6626 | #endif | 6757 | #endif |
6627 | 6758 | ||
6628 | do { | 6759 | helper_export_local(argv, (opt_unexport ? -1 : 1), 0); |
6629 | char *name = *argv; | ||
6630 | 6760 | ||
6631 | /* So far we do not check that name is valid (TODO?) */ | 6761 | return EXIT_SUCCESS; |
6632 | 6762 | } | |
6633 | if (strchr(name, '=') == NULL) { | ||
6634 | struct variable *var; | ||
6635 | |||
6636 | var = get_local_var(name); | ||
6637 | if (opt_unexport) { | ||
6638 | /* export -n NAME (without =VALUE) */ | ||
6639 | if (var) { | ||
6640 | var->flg_export = 0; | ||
6641 | debug_printf_env("%s: unsetenv '%s'\n", __func__, name); | ||
6642 | unsetenv(name); | ||
6643 | } /* else: export -n NOT_EXISTING_VAR: no-op */ | ||
6644 | continue; | ||
6645 | } | ||
6646 | /* export NAME (without =VALUE) */ | ||
6647 | if (var) { | ||
6648 | var->flg_export = 1; | ||
6649 | debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr); | ||
6650 | putenv(var->varstr); | ||
6651 | continue; | ||
6652 | } | ||
6653 | /* Exporting non-existing variable. | ||
6654 | * bash does not put it in environment, | ||
6655 | * but remembers that it is exported, | ||
6656 | * and does put it in env when it is set later. | ||
6657 | * We just set it to "" and export. */ | ||
6658 | name = xasprintf("%s=", name); | ||
6659 | } else { | ||
6660 | /* (Un)exporting NAME=VALUE */ | ||
6661 | name = xstrdup(name); | ||
6662 | } | ||
6663 | set_local_var(name, | ||
6664 | /*export:*/ (opt_unexport ? -1 : 1), | ||
6665 | /*readonly:*/ 0 | ||
6666 | ); | ||
6667 | } while (*++argv); | ||
6668 | 6763 | ||
6764 | #if ENABLE_HUSH_LOCAL | ||
6765 | static int builtin_local(char **argv) | ||
6766 | { | ||
6767 | if (G.func_nest_level == 0) { | ||
6768 | bb_error_msg("%s: not in a function", argv[0]); | ||
6769 | return EXIT_FAILURE; /* bash compat */ | ||
6770 | } | ||
6771 | helper_export_local(argv, 0, G.func_nest_level); | ||
6669 | return EXIT_SUCCESS; | 6772 | return EXIT_SUCCESS; |
6670 | } | 6773 | } |
6774 | #endif | ||
6671 | 6775 | ||
6672 | static int builtin_trap(char **argv) | 6776 | static int builtin_trap(char **argv) |
6673 | { | 6777 | { |
@@ -6944,7 +7048,7 @@ static int builtin_read(char **argv) | |||
6944 | //TODO: bash unbackslashes input, splits words and puts them in argv[i] | 7048 | //TODO: bash unbackslashes input, splits words and puts them in argv[i] |
6945 | 7049 | ||
6946 | string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL); | 7050 | string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL); |
6947 | return set_local_var(string, 0, 0); | 7051 | return set_local_var(string, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0); |
6948 | } | 7052 | } |
6949 | 7053 | ||
6950 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set | 7054 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set |
diff --git a/shell/hush_test/hush-misc/func_local1.right b/shell/hush_test/hush-misc/func_local1.right new file mode 100644 index 000000000..312178366 --- /dev/null +++ b/shell/hush_test/hush-misc/func_local1.right | |||
@@ -0,0 +1,3 @@ | |||
1 | z=a | ||
2 | z=z | ||
3 | Done | ||
diff --git a/shell/hush_test/hush-misc/func_local1.tests b/shell/hush_test/hush-misc/func_local1.tests new file mode 100755 index 000000000..1d594e20c --- /dev/null +++ b/shell/hush_test/hush-misc/func_local1.tests | |||
@@ -0,0 +1,5 @@ | |||
1 | export z=z | ||
2 | f() { local z=a; env | grep ^z; } | ||
3 | f | ||
4 | env | grep ^z | ||
5 | echo Done | ||
diff --git a/shell/hush_test/hush-misc/func_local2.right b/shell/hush_test/hush-misc/func_local2.right new file mode 100644 index 000000000..fe9343ac8 --- /dev/null +++ b/shell/hush_test/hush-misc/func_local2.right | |||
@@ -0,0 +1,14 @@ | |||
1 | 1 | ||
2 | 2 | ||
3 | 1 | ||
4 | 2 | ||
5 | 1 | ||
6 | 1 | ||
7 | 2 | ||
8 | 2 | ||
9 | 3 | ||
10 | 2 | ||
11 | 2 | ||
12 | 3 | ||
13 | 1 | ||
14 | Done | ||
diff --git a/shell/hush_test/hush-misc/func_local2.tests b/shell/hush_test/hush-misc/func_local2.tests new file mode 100755 index 000000000..1a9ae559d --- /dev/null +++ b/shell/hush_test/hush-misc/func_local2.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | x=1 | ||
2 | f() { echo $x; local x=$((x+1)); echo $x; } | ||
3 | g() { f; echo $x; f; local x=$((x+1)); f; echo $x; f; } | ||
4 | f | ||
5 | g | ||
6 | echo $x | ||
7 | echo Done | ||