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 <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-07-17 21:10:50 +02:00
parent 826360ff23
commit 1e660422b1

View File

@ -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)