From 1e660422b16510f8bcdb764d632bb1da391c4a35 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 17 Jul 2017 21:10:50 +0200 Subject: hush: implement "readonly" builtin function old new delta builtin_readonly - 70 +70 helper_export_local 152 174 +22 bltins1 348 360 +12 expand_one_var 1620 1625 +5 builtin_export 168 173 +5 set_pwd_var 36 40 +4 set_local_var 410 414 +4 set_vars_and_save_old 85 88 +3 set_local_var_from_halves 24 27 +3 run_pipe 1551 1554 +3 run_list 1046 1048 +2 builtin_type 116 114 -2 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 10/1 up/down: 133/-2) Total: 131 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 99 ++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index c8356f4b8..a68986329 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -48,7 +48,7 @@ * tilde expansion * aliases * builtins mandated by standards we don't support: - * [un]alias, command, fc, getopts, readonly, times: + * [un]alias, command, fc, getopts, times: * command -v CMD: print "/path/to/CMD" * prints "CMD" for builtins * prints "alias ALIAS='EXPANSION'" for aliases @@ -57,8 +57,7 @@ * command [-p] CMD: run CMD, even if a function CMD also exists * (can use this to override standalone shell as well) * -p: use default $PATH - * readonly VAR[=VAL]...: make VARs readonly - * readonly [-p]: list all such VARs (-p has no effect in bash) + * command BLTIN: disables special-ness (e.g. errors do not abort) * getopts: getopt() for shells * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime * fc -l[nr] [BEG] [END]: list range of commands in history @@ -239,6 +238,12 @@ //config: help //config: export -n unexports variables. It is a bash extension. //config: +//config:config HUSH_READONLY +//config: bool "readonly builtin" +//config: default y +//config: help +//config: Enable support for read-only variables. +//config: //config:config HUSH_KILL //config: bool "kill builtin (supports kill %jobspec)" //config: default y @@ -964,6 +969,9 @@ static int builtin_exit(char **argv) FAST_FUNC; #if ENABLE_HUSH_EXPORT static int builtin_export(char **argv) FAST_FUNC; #endif +#if ENABLE_HUSH_READONLY +static int builtin_readonly(char **argv) FAST_FUNC; +#endif #if ENABLE_HUSH_JOB static int builtin_fg_bg(char **argv) FAST_FUNC; static int builtin_jobs(char **argv) FAST_FUNC; @@ -1082,6 +1090,9 @@ static const struct built_in_command bltins1[] = { #if ENABLE_HUSH_READ BLTIN("read" , builtin_read , "Input into variable"), #endif +#if ENABLE_HUSH_READONLY + BLTIN("readonly" , builtin_readonly, "Make variables read-only"), +#endif #if ENABLE_HUSH_FUNCTIONS BLTIN("return" , builtin_return , "Return from function"), #endif @@ -2052,19 +2063,10 @@ static const char* FAST_FUNC get_local_var_value(const char *name) * -1: clear export flag and unsetenv the variable * flg_read_only is set only when we handle -R var=val */ -#if !BB_MMU && ENABLE_HUSH_LOCAL -/* all params are used */ -#elif BB_MMU && ENABLE_HUSH_LOCAL -#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ - set_local_var(str, flg_export, local_lvl) -#elif BB_MMU && !ENABLE_HUSH_LOCAL -#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ - set_local_var(str, flg_export) -#elif !BB_MMU && !ENABLE_HUSH_LOCAL -#define set_local_var(str, flg_export, local_lvl, flg_read_only) \ - set_local_var(str, flg_export, flg_read_only) -#endif -static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only) +static int set_local_var(char *str, + int flg_export UNUSED_PARAM, + int local_lvl UNUSED_PARAM, + int flg_read_only UNUSED_PARAM) { struct variable **var_pp; struct variable *cur; @@ -2088,9 +2090,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ /* We found an existing var with this name */ if (cur->flg_read_only) { -#if !BB_MMU if (!flg_read_only) -#endif bb_error_msg("%s: readonly variable", str); free(str); return -1; @@ -2158,10 +2158,12 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_ set_str_and_exp: cur->varstr = str; -#if !BB_MMU - cur->flg_read_only = flg_read_only; -#endif exp: +#if !BB_MMU || ENABLE_HUSH_READONLY + if (flg_read_only != 0) { + cur->flg_read_only = flg_read_only; + } +#endif if (flg_export == 1) cur->flg_export = 1; if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S') @@ -9308,12 +9310,11 @@ static void print_escaped(const char *s) } #endif -#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL -# if !ENABLE_HUSH_LOCAL -#define helper_export_local(argv, exp, lvl) \ - helper_export_local(argv, exp) -# endif -static void helper_export_local(char **argv, int exp, int lvl) +#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY +static int helper_export_local(char **argv, + int exp UNUSED_PARAM, + int ro UNUSED_PARAM, + int lvl UNUSED_PARAM) { do { char *name = *argv; @@ -9346,7 +9347,7 @@ static void helper_export_local(char **argv, int exp, int lvl) } } # if ENABLE_HUSH_LOCAL - if (exp == 0 /* local? */ + if (exp == 0 && ro == 0 /* local? */ && var && var->func_nest_level == lvl ) { /* "local x=abc; ...; local x" - ignore second local decl */ @@ -9357,16 +9358,23 @@ static void helper_export_local(char **argv, int exp, int lvl) * bash does not put it in environment, * but remembers that it is exported, * and does put it in env when it is set later. - * We just set it to "" and export. */ + * We just set it to "" and export. + */ /* Or, it's "local NAME" (without =VALUE). - * bash sets the value to "". */ + * bash sets the value to "". + */ + /* Or, it's "readonly NAME" (without =VALUE). + * bash remembers NAME and disallows its creation + * in the future. + */ name = xasprintf("%s=", name); } else { /* (Un)exporting/making local NAME=VALUE */ name = xstrdup(name); } - set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0); + set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ ro); } while (*++argv); + return EXIT_SUCCESS; } #endif @@ -9412,9 +9420,7 @@ static int FAST_FUNC builtin_export(char **argv) return EXIT_SUCCESS; } - helper_export_local(argv, (opt_unexport ? -1 : 1), 0); - - return EXIT_SUCCESS; + return helper_export_local(argv, /*exp:*/ (opt_unexport ? -1 : 1), /*ro:*/ 0, /*lvl:*/ 0); } #endif @@ -9425,11 +9431,32 @@ static int FAST_FUNC builtin_local(char **argv) bb_error_msg("%s: not in a function", argv[0]); return EXIT_FAILURE; /* bash compat */ } - helper_export_local(argv, 0, G.func_nest_level); - return EXIT_SUCCESS; + argv++; + return helper_export_local(argv, /*exp:*/ 0, /*ro:*/ 0, /*lvl:*/ G.func_nest_level); } #endif +#if ENABLE_HUSH_READONLY +static int FAST_FUNC builtin_readonly(char **argv) +{ + if (*++argv == NULL) { + /* bash: readonly [-p]: list all readonly VARs + * (-p has no effect in bash) + */ + struct variable *e; + for (e = G.top_var; e; e = e->next) { + if (e->flg_read_only) { +//TODO: quote value: readonly VAR='VAL' + printf("readonly %s\n", e->varstr); + } + } + return EXIT_SUCCESS; + } + return helper_export_local(argv, /*exp:*/ 0, /*ro:*/ 1, /*lvl:*/ 0); +} +#endif + + #if ENABLE_HUSH_UNSET /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */ static int FAST_FUNC builtin_unset(char **argv) -- cgit v1.2.3