aboutsummaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2018-04-05 14:09:14 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2018-04-05 14:09:14 +0200
commit929a41d5770c0531f037c2e7db25bf98f9029c9e (patch)
treeab4a79bd1784435b1bbc3488145e7129452b8cdc /shell/hush.c
parent4e1dc539e97300c44589eff8baffd12c95a13d5f (diff)
downloadbusybox-w32-929a41d5770c0531f037c2e7db25bf98f9029c9e.tar.gz
busybox-w32-929a41d5770c0531f037c2e7db25bf98f9029c9e.tar.bz2
busybox-w32-929a41d5770c0531f037c2e7db25bf98f9029c9e.zip
hush: less mind-bending set_vars_and_save_old()
function old new delta run_pipe 1651 1701 +50 set_local_var 510 557 +47 pseudo_exec_argv 544 581 +37 redirect_and_varexp_helper 64 56 -8 set_vars_and_save_old 164 149 -15 unset_local_var 274 256 -18 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/3 up/down: 134/-41) Total: 93 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to '')
-rw-r--r--shell/hush.c172
1 files changed, 101 insertions, 71 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 4740929e8..24ae237d7 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -2140,19 +2140,10 @@ static int set_local_var(char *str, unsigned flags)
2140 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT); 2140 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
2141 2141
2142 eq_sign = strchr(str, '='); 2142 eq_sign = strchr(str, '=');
2143 if (!eq_sign) { /* not expected to ever happen? */ 2143 if (HUSH_DEBUG && !eq_sign)
2144 free(str); 2144 bb_error_msg_and_die("BUG in setvar");
2145 return -1;
2146 }
2147 2145
2148 name_len = eq_sign - str + 1; /* including '=' */ 2146 name_len = eq_sign - str + 1; /* including '=' */
2149#if ENABLE_HUSH_LINENO_VAR
2150 if (G.lineno_var) {
2151 if (name_len == 7 && strncmp("LINENO", str, 6) == 0)
2152 G.lineno_var = NULL;
2153 }
2154#endif
2155
2156 cur_pp = &G.top_var; 2147 cur_pp = &G.top_var;
2157 while ((cur = *cur_pp) != NULL) { 2148 while ((cur = *cur_pp) != NULL) {
2158 if (strncmp(cur->varstr, str, name_len) != 0) { 2149 if (strncmp(cur->varstr, str, name_len) != 0) {
@@ -2175,19 +2166,6 @@ static int set_local_var(char *str, unsigned flags)
2175 *eq_sign = '='; 2166 *eq_sign = '=';
2176 } 2167 }
2177 if (cur->var_nest_level < local_lvl) { 2168 if (cur->var_nest_level < local_lvl) {
2178 /* New variable is local ("local VAR=VAL" or
2179 * "VAR=VAL cmd")
2180 * and existing one is global, or local
2181 * on a lower level that new one.
2182 * Remove and save old one:
2183 */
2184 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2185 cur->flg_export ? "exported " : "",
2186 cur->varstr, cur->var_nest_level, str, local_lvl
2187 );
2188 *cur_pp = cur->next;
2189 cur->next = *G.shadowed_vars_pp;
2190 *G.shadowed_vars_pp = cur;
2191 /* bash 3.2.33(1) and exported vars: 2169 /* bash 3.2.33(1) and exported vars:
2192 * # export z=z 2170 * # export z=z
2193 * # f() { local z=a; env | grep ^z; } 2171 * # f() { local z=a; env | grep ^z; }
@@ -2198,6 +2176,31 @@ static int set_local_var(char *str, unsigned flags)
2198 */ 2176 */
2199 if (cur->flg_export) 2177 if (cur->flg_export)
2200 flags |= SETFLAG_EXPORT; 2178 flags |= SETFLAG_EXPORT;
2179 /* New variable is local ("local VAR=VAL" or
2180 * "VAR=VAL cmd")
2181 * and existing one is global, or local
2182 * on a lower level that new one.
2183 * Remove it from global variable list:
2184 */
2185 *cur_pp = cur->next;
2186 if (G.shadowed_vars_pp) {
2187 /* Save in "shadowed" list */
2188 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2189 cur->flg_export ? "exported " : "",
2190 cur->varstr, cur->var_nest_level, str, local_lvl
2191 );
2192 cur->next = *G.shadowed_vars_pp;
2193 *G.shadowed_vars_pp = cur;
2194 } else {
2195 /* Came from pseudo_exec_argv(), no need to save: delete it */
2196 debug_printf_env("shadow-deleting %s'%s'/%u by '%s'/%u\n",
2197 cur->flg_export ? "exported " : "",
2198 cur->varstr, cur->var_nest_level, str, local_lvl
2199 );
2200 if (cur->max_len == 0) /* allocated "VAR=VAL"? */
2201 free_me = cur->varstr; /* then free it later */
2202 free(cur);
2203 }
2201 break; 2204 break;
2202 } 2205 }
2203 2206
@@ -2207,8 +2210,10 @@ static int set_local_var(char *str, unsigned flags)
2207 free(str); 2210 free(str);
2208 goto exp; 2211 goto exp;
2209 } 2212 }
2213
2214 /* Replace the value in the found "struct variable" */
2210 if (cur->max_len != 0) { 2215 if (cur->max_len != 0) {
2211 if (cur->max_len >= strlen(str)) { 2216 if (cur->max_len >= strnlen(str, cur->max_len + 1)) {
2212 /* This one is from startup env, reuse space */ 2217 /* This one is from startup env, reuse space */
2213 debug_printf_env("reusing startup env for '%s'\n", str); 2218 debug_printf_env("reusing startup env for '%s'\n", str);
2214 strcpy(cur->varstr, str); 2219 strcpy(cur->varstr, str);
@@ -2244,13 +2249,6 @@ static int set_local_var(char *str, unsigned flags)
2244#endif 2249#endif
2245 if (flags & SETFLAG_EXPORT) 2250 if (flags & SETFLAG_EXPORT)
2246 cur->flg_export = 1; 2251 cur->flg_export = 1;
2247 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
2248 cmdedit_update_prompt();
2249#if ENABLE_HUSH_GETOPTS
2250 /* defoptindvar is a "OPTIND=..." constant string */
2251 if (strncmp(cur->varstr, defoptindvar, 7) == 0)
2252 G.getopt_count = 0;
2253#endif
2254 if (cur->flg_export) { 2252 if (cur->flg_export) {
2255 if (flags & SETFLAG_UNEXPORT) { 2253 if (flags & SETFLAG_UNEXPORT) {
2256 cur->flg_export = 0; 2254 cur->flg_export = 0;
@@ -2265,6 +2263,23 @@ static int set_local_var(char *str, unsigned flags)
2265 } 2263 }
2266 } 2264 }
2267 free(free_me); 2265 free(free_me);
2266
2267 /* Handle special names */
2268 if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
2269 cmdedit_update_prompt();
2270 else
2271 if ((ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) && name_len == 7) {
2272#if ENABLE_HUSH_LINENO_VAR
2273 if (G.lineno_var && strncmp(cur->varstr, "LINENO", 6) == 0)
2274 G.lineno_var = NULL;
2275#endif
2276#if ENABLE_HUSH_GETOPTS
2277 /* defoptindvar is a "OPTIND=..." constant string */
2278 if (strncmp(cur->varstr, defoptindvar, 7) == 0)
2279 G.getopt_count = 0;
2280#endif
2281 }
2282
2268 return 0; 2283 return 0;
2269} 2284}
2270 2285
@@ -2282,14 +2297,16 @@ static int unset_local_var_len(const char *name, int name_len)
2282 if (!name) 2297 if (!name)
2283 return EXIT_SUCCESS; 2298 return EXIT_SUCCESS;
2284 2299
2300 if ((ENABLE_HUSH_LINENO_VAR || ENABLE_HUSH_GETOPTS) && name_len == 6) {
2285#if ENABLE_HUSH_GETOPTS 2301#if ENABLE_HUSH_GETOPTS
2286 if (name_len == 6 && strncmp(name, "OPTIND", 6) == 0) 2302 if (strncmp(name, "OPTIND", 6) == 0)
2287 G.getopt_count = 0; 2303 G.getopt_count = 0;
2288#endif 2304#endif
2289#if ENABLE_HUSH_LINENO_VAR 2305#if ENABLE_HUSH_LINENO_VAR
2290 if (name_len == 6 && G.lineno_var && strncmp(name, "LINENO", 6) == 0) 2306 if (G.lineno_var && strncmp(name, "LINENO", 6) == 0)
2291 G.lineno_var = NULL; 2307 G.lineno_var = NULL;
2292#endif 2308#endif
2309 }
2293 2310
2294 cur_pp = &G.top_var; 2311 cur_pp = &G.top_var;
2295 while ((cur = *cur_pp) != NULL) { 2312 while ((cur = *cur_pp) != NULL) {
@@ -2355,13 +2372,12 @@ static void add_vars(struct variable *var)
2355 * which attempts to overwrite it. 2372 * which attempts to overwrite it.
2356 * The strings[] vector itself is freed. 2373 * The strings[] vector itself is freed.
2357 */ 2374 */
2358static struct variable *set_vars_and_save_old(char **strings) 2375static void set_vars_and_save_old(char **strings)
2359{ 2376{
2360 char **s; 2377 char **s;
2361 struct variable *old = NULL;
2362 2378
2363 if (!strings) 2379 if (!strings)
2364 return old; 2380 return;
2365 2381
2366 s = strings; 2382 s = strings;
2367 while (*s) { 2383 while (*s) {
@@ -2389,12 +2405,10 @@ static struct variable *set_vars_and_save_old(char **strings)
2389 do { *p = p[1]; p++; } while (*p); 2405 do { *p = p[1]; p++; } while (*p);
2390 goto next; 2406 goto next;
2391 } 2407 }
2392 /* Remove variable from global linked list */ 2408 /* below, set_local_var() with nest level will
2393 debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr); 2409 * "shadow" (remove) this variable from
2394 *var_pp = var_p->next; 2410 * global linked list.
2395 /* Add it to returned list */ 2411 */
2396 var_p->next = old;
2397 old = var_p;
2398 } 2412 }
2399 //bb_error_msg("G.var_nest_level:%d", G.var_nest_level); 2413 //bb_error_msg("G.var_nest_level:%d", G.var_nest_level);
2400 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT); 2414 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT);
@@ -2405,7 +2419,6 @@ static struct variable *set_vars_and_save_old(char **strings)
2405 next: ; 2419 next: ;
2406 } 2420 }
2407 free(strings); 2421 free(strings);
2408 return old;
2409} 2422}
2410 2423
2411 2424
@@ -7598,6 +7611,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7598 char **argv_expanded) 7611 char **argv_expanded)
7599{ 7612{
7600 const struct built_in_command *x; 7613 const struct built_in_command *x;
7614 struct variable **sv_shadowed;
7601 char **new_env; 7615 char **new_env;
7602 IF_HUSH_COMMAND(char opt_vV = 0;) 7616 IF_HUSH_COMMAND(char opt_vV = 0;)
7603 IF_HUSH_FUNCTIONS(const struct function *funcp;) 7617 IF_HUSH_FUNCTIONS(const struct function *funcp;)
@@ -7614,13 +7628,14 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
7614 _exit(EXIT_SUCCESS); 7628 _exit(EXIT_SUCCESS);
7615 } 7629 }
7616 7630
7631 sv_shadowed = G.shadowed_vars_pp;
7617#if BB_MMU 7632#if BB_MMU
7618 set_vars_and_save_old(new_env); 7633 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
7619 /* we can destroy set_vars_and_save_old's return value,
7620 * to save memory */
7621#else 7634#else
7622 nommu_save->old_vars = set_vars_and_save_old(new_env); 7635 G.shadowed_vars_pp = &nommu_save->old_vars;
7623#endif 7636#endif
7637 set_vars_and_save_old(new_env);
7638 G.shadowed_vars_pp = sv_shadowed;
7624 7639
7625 if (argv_expanded) { 7640 if (argv_expanded) {
7626 argv = argv_expanded; 7641 argv = argv_expanded;
@@ -8181,7 +8196,6 @@ static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
8181 redirect_and_varexp_helper(old_vars_p, command, squirrel) 8196 redirect_and_varexp_helper(old_vars_p, command, squirrel)
8182#endif 8197#endif
8183static int redirect_and_varexp_helper( 8198static int redirect_and_varexp_helper(
8184 struct variable **old_vars_p,
8185 struct command *command, 8199 struct command *command,
8186 struct squirrel **sqp, 8200 struct squirrel **sqp,
8187 char **argv_expanded) 8201 char **argv_expanded)
@@ -8196,7 +8210,7 @@ static int redirect_and_varexp_helper(
8196 dump_cmd_in_x_mode(new_env); 8210 dump_cmd_in_x_mode(new_env);
8197 dump_cmd_in_x_mode(argv_expanded); 8211 dump_cmd_in_x_mode(argv_expanded);
8198 /* this takes ownership of new_env[i] elements, and frees new_env: */ 8212 /* this takes ownership of new_env[i] elements, and frees new_env: */
8199 *old_vars_p = set_vars_and_save_old(new_env); 8213 set_vars_and_save_old(new_env);
8200 } 8214 }
8201 return rcode; 8215 return rcode;
8202} 8216}
@@ -8288,6 +8302,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
8288 const struct built_in_command *x; 8302 const struct built_in_command *x;
8289 IF_HUSH_FUNCTIONS(const struct function *funcp;) 8303 IF_HUSH_FUNCTIONS(const struct function *funcp;)
8290 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };) 8304 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
8305 struct variable **sv_shadowed;
8291 struct variable *old_vars; 8306 struct variable *old_vars;
8292 8307
8293#if ENABLE_HUSH_LINENO_VAR 8308#if ENABLE_HUSH_LINENO_VAR
@@ -8338,13 +8353,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
8338 8353
8339 /* Expand the rest into (possibly) many strings each */ 8354 /* Expand the rest into (possibly) many strings each */
8340#if defined(CMD_SINGLEWORD_NOGLOB) 8355#if defined(CMD_SINGLEWORD_NOGLOB)
8341 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB) { 8356 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
8342 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt); 8357 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
8343 } else 8358 else
8344#endif 8359#endif
8345 {
8346 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); 8360 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
8347 }
8348 8361
8349 /* if someone gives us an empty string: `cmd with empty output` */ 8362 /* if someone gives us an empty string: `cmd with empty output` */
8350//TODO: what about: var=EXPR `` >FILE ? will var be set? Will FILE be created? 8363//TODO: what about: var=EXPR `` >FILE ? will var be set? Will FILE be created?
@@ -8354,17 +8367,19 @@ static NOINLINE int run_pipe(struct pipe *pi)
8354 return G.last_exitcode; 8367 return G.last_exitcode;
8355 } 8368 }
8356 8369
8357#if ENABLE_HUSH_FUNCTIONS 8370 old_vars = NULL;
8371 sv_shadowed = G.shadowed_vars_pp;
8372
8358 /* Check if argv[0] matches any functions (this goes before bltins) */ 8373 /* Check if argv[0] matches any functions (this goes before bltins) */
8359 funcp = find_function(argv_expanded[0]); 8374 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
8360#endif 8375 IF_HUSH_FUNCTIONS(x = NULL;)
8361 x = NULL; 8376 IF_HUSH_FUNCTIONS(if (!funcp))
8362 if (!funcp)
8363 x = find_builtin(argv_expanded[0]); 8377 x = find_builtin(argv_expanded[0]);
8364 if (x || funcp) { 8378 if (x || funcp) {
8365 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) { 8379 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
8366 debug_printf("exec with redirects only\n"); 8380 debug_printf("exec with redirects only\n");
8367 rcode = setup_redirects(command, NULL); 8381 rcode = setup_redirects(command, NULL);
8382//TODO: what about: var=EXPR exec >FILE ? will var be set?
8368 /* rcode=1 can be if redir file can't be opened */ 8383 /* rcode=1 can be if redir file can't be opened */
8369 goto clean_up_and_ret1; 8384 goto clean_up_and_ret1;
8370 } 8385 }
@@ -8373,9 +8388,22 @@ static NOINLINE int run_pipe(struct pipe *pi)
8373 * a=b true; env | grep ^a= 8388 * a=b true; env | grep ^a=
8374 */ 8389 */
8375 enter_var_nest_level(); 8390 enter_var_nest_level();
8376 rcode = redirect_and_varexp_helper(&old_vars, command, &squirrel, argv_expanded); 8391 /* Collect all variables "shadowed" by helper
8392 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
8393 * into old_vars list:
8394 */
8395 G.shadowed_vars_pp = &old_vars;
8396 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
8377 if (rcode == 0) { 8397 if (rcode == 0) {
8378 if (!funcp) { 8398 if (!funcp) {
8399 /* Do not collect *to old_vars list* vars shadowed
8400 * by e.g. "local VAR" builtin (collect them
8401 * in the previously nested list instead):
8402 * don't want them to be restored immediately
8403 * after "local" completes.
8404 */
8405 G.shadowed_vars_pp = sv_shadowed;
8406
8379 debug_printf_exec(": builtin '%s' '%s'...\n", 8407 debug_printf_exec(": builtin '%s' '%s'...\n",
8380 x->b_cmd, argv_expanded[1]); 8408 x->b_cmd, argv_expanded[1]);
8381 fflush_all(); 8409 fflush_all();
@@ -8384,17 +8412,15 @@ static NOINLINE int run_pipe(struct pipe *pi)
8384 } 8412 }
8385#if ENABLE_HUSH_FUNCTIONS 8413#if ENABLE_HUSH_FUNCTIONS
8386 else { 8414 else {
8387 struct variable **sv = G.shadowed_vars_pp;
8388 /* Make "local" builtins in the called function
8389 * stash shadowed variables in old_vars list
8390 */
8391 G.shadowed_vars_pp = &old_vars;
8392
8393 debug_printf_exec(": function '%s' '%s'...\n", 8415 debug_printf_exec(": function '%s' '%s'...\n",
8394 funcp->name, argv_expanded[1]); 8416 funcp->name, argv_expanded[1]);
8395 rcode = run_function(funcp, argv_expanded) & 0xff; 8417 rcode = run_function(funcp, argv_expanded) & 0xff;
8396 8418 /*
8397 G.shadowed_vars_pp = sv; 8419 * But do collect *to old_vars list* vars shadowed
8420 * within function execution. To that end, restore
8421 * this pointer _after_ function run:
8422 */
8423 G.shadowed_vars_pp = sv_shadowed;
8398 } 8424 }
8399#endif 8425#endif
8400 } 8426 }
@@ -8405,7 +8431,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
8405 goto must_fork; 8431 goto must_fork;
8406 8432
8407 enter_var_nest_level(); 8433 enter_var_nest_level();
8408 rcode = redirect_and_varexp_helper(&old_vars, command, &squirrel, argv_expanded); 8434 /* Collect all variables "shadowed" by helper into old_vars list */
8435 G.shadowed_vars_pp = &old_vars;
8436 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
8437 G.shadowed_vars_pp = sv_shadowed;
8438
8409 if (rcode == 0) { 8439 if (rcode == 0) {
8410 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 8440 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
8411 argv_expanded[0], argv_expanded[1]); 8441 argv_expanded[0], argv_expanded[1]);