diff options
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | doc/pies.texi | 15 | ||||
-rw-r--r-- | lib/envop.c | 51 | ||||
-rw-r--r-- | src/pies.c | 22 |
4 files changed, 77 insertions, 19 deletions
@@ -1,13 +1,13 @@ -GNU Pies NEWS -- history of user-visible changes. 2019-06-06 +GNU Pies NEWS -- history of user-visible changes. 2019-06-07 See the end of file for copying conditions. Please send Pies bug reports to <bug-pies@gnu.org> or <bug-pies@gnu.org.ua> Version 1.3.91 (git) * New option --no-init The --no-init option instructs pies not to switch to init mode if its PID is 1. Use this option if you run pies as a process manager in a docker container. @@ -46,24 +46,30 @@ It can contain the following sub-statements: ** clear Clears the environment ** keep NAME Keeps the variable NAME when clearing the environment. Implies "clear". NAME can be a globbing pattern, in which case all variables matching the pattern are retained. ** set "NAME=VALUE" Sets the environment variable for the component. VALUE is subject to variable expansion. +** eval "VALUE" +Perform variable expansion on VALUE and discard the result (similar +to the shell ":" command). Useful for side effects, e.g.: + + eval ${HOME:=/home/t} + ** unset NAME Unsets the variable. NAME can be a globbing pattern, in which case all variables matching the pattern are unset. Example: env { clear; keep PATH; keep MANPATH; keep "LC_*"; set "MANPATH=$MANPATH${MANPATH:+:}/usr/local/man"; diff --git a/doc/pies.texi b/doc/pies.texi index 63064e5..4bd2f8f 100644 --- a/doc/pies.texi +++ b/doc/pies.texi @@ -1055,24 +1055,25 @@ up to 1.3 and is still retained for backward compatibility. It is described in @ref{env legacy syntax}. This subsection describes the modern compount syntax. @deffn {Config: component} env @{ ... @} The compound @code{env} statement has the following syntax: @example @group env @{ clear; keep @var{pattern}; set "@var{name}=@var{value}"; + eval "@var{value}"; unset @var{pattern}; @} @end group @end example @end deffn Statements inside the @code{env} block define operations that modify the environment. The @code{clear} and @code{keep} statements are executed first. Then, the @code{set} and @code{unset} statements are applied in the order of their appearance in the configuration. @deffn {env} clear @@ -1114,24 +1115,38 @@ The @code{set} and @code{unset} (see below) statements are executed in order of their appearance. For example @example @group env @{ set "MYLIB=$HOME/lib"; set "LD_LIBRARY_PATH=$LD_LIBRARY_PATH$@{LD_LIBRARY_PATH:+:@}$MYLIB"; @} @end group @end example @end deffn +@deffn {env} eval "@var{value}" +Perform variable expansion on @var{value} and discard the result. This +is useful for side-effects. For example, to provide default value for +the @env{LD_LIBRARY_PATH} variable, one may write: + +@example +@group +env @{ + eval "$@{LD_LIBRARY_PATH:=/usr/local/lib@}"; +@} +@end group +@end example +@end deffn + @deffn {env} unset @var{pattern} Unset environment variables matching @var{pattern}. The following will unset the @env{LOGIN} variable: @example unset LOGIN; @end example @noindent The following will unset all variables starting with @samp{LD_}: @example diff --git a/lib/envop.c b/lib/envop.c index ce24b92..93bd425 100644 --- a/lib/envop.c +++ b/lib/envop.c @@ -158,49 +158,62 @@ environ_add (environ_t *env, char const *def) return -1; } return 0; } int environ_set (environ_t *env, char const *name, char const *value) { size_t len; char *def; struct wordsplit ws; - if (!name) + if (!value) { - errno = EINVAL; - return -1; + if (!name) + { + errno = EINVAL; + return -1; + } + return environ_unset (env, name, value); } - if (!value) - return environ_unset (env, name, value); ws.ws_env = (char const **) env->env_base; if (wordsplit (value, &ws, WRDSF_NOSPLIT | WRDSF_QUOTE | WRDSF_NOCMD /* FIXME */ | WRDSF_SQUEEZE_DELIMS | WRDSF_CESCAPES | WRDSF_ENV | WRDSF_PATHEXPAND)) { int ec = errno; wordsplit_free (&ws); errno = ec; return -1; } - if (strcmp (name, ":") == 0) + if (ws.ws_envbuf) + { + free (env->env_base); + env->env_base = ws.ws_envbuf; + env->env_count = ws.ws_envidx; + env->env_max = ws.ws_envsiz; + ws.ws_envbuf = NULL; + ws.ws_envidx = 0; + ws.ws_envsiz = 0; + } + + if (name == NULL || strcmp (name, ":") == 0) { wordsplit_free (&ws); return 0; } len = strlen (name) + strlen (ws.ws_wordv[0]) + 2; def = malloc (len); if (!def) { int ec = errno; wordsplit_free (&ws); errno = ec; @@ -325,70 +338,72 @@ valid_envar_name (char const *name) if (!(isalnum (*name) || *name == '_')) return 0; } return 1; } int envop_entry_add (struct envop_entry **head, enum envop_code code, char const *name, char const *value) { struct envop_entry *op; size_t s; + char *p; switch (code) { case envop_clear: break; case envop_set: - if (!name || !(*name == ':' || valid_envar_name (name))) + if (name && !(*name == ':' || valid_envar_name (name))) { errno = EINVAL; return -1; } break; case envop_keep: case envop_unset: break; default: errno = EINVAL; return -1; } s = sizeof (op[0]); if (name) - { - s += strlen (name) + 1; - if (value) - s += strlen (value) + 1; - } + s += strlen (name) + 1; + if (value) + s += strlen (value) + 1; op = malloc (s); if (!op) return -1; op->next = NULL; op->code = code; op->name = NULL; op->value = NULL; + + p = (char*)(op + 1); if (name) { - op->name = (char*)(op + 1); + op->name = p; strcpy (op->name, name); - if (value) - { - op->value = op->name + strlen (name) + 1; - strcpy (op->value, value); - } + p += strlen (name) + 1; + } + if (value) + { + op->value = p; + strcpy (op->value, value); } envop_entry_insert (head, op); return 0; } static int envopmatch (struct envop_entry *op, char const *var, int len) { if (op->value) { if (strncmp (op->name, var, len) == 0) return strcmp (var + len + 1, op->value); @@ -777,24 +777,40 @@ _cb_env_set (enum grecs_callback_command cmd, if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING)) return 1; p = strchr (value->v.string, '='); if (p) *p++ = 0; if (envop_entry_add (&comp->envop, envop_set, value->v.string, p)) grecs_error (locus, errno, "envop_entry_add"); return 0; } static int +_cb_env_eval (enum grecs_callback_command cmd, + grecs_node_t *node, + void *varptr, void *cb_data) +{ + grecs_locus_t *locus = &node->locus; + grecs_value_t *value = node->v.value; + struct component *comp = varptr; + + if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING)) + return 1; + if (envop_entry_add (&comp->envop, envop_set, NULL, value->v.string)) + grecs_error (locus, errno, "envop_entry_add"); + return 0; +} + +static int _cb_env_unset (enum grecs_callback_command cmd, grecs_node_t *node, void *varptr, void *cb_data) { grecs_locus_t *locus = &node->locus; grecs_value_t *value = node->v.value; struct component *comp = varptr; char *p; if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING)) return 1; p = strchr (value->v.string, '='); @@ -816,24 +832,30 @@ struct grecs_keyword cb_env_keywords[] = { N_("name[=value]"), N_("Keep this variable. Unless value is supplied, name can contain wildcards.\n" "Implies \"clear yes\"."), grecs_type_string, GRECS_DFLT, NULL, 0, _cb_env_keep }, { "set", N_("name=value"), N_("Set environment variable. Note, that argument must be quoted."), grecs_type_string, GRECS_DFLT, NULL, 0, _cb_env_set }, + { "eval", + N_("string"), + N_("Evaluate string. Useful for side-effects, e.g. eval ${X:=2}."), + grecs_type_string, GRECS_DFLT, + NULL, 0, + _cb_env_eval }, { "unset", N_("name"), N_("Unset environment variable. Name can contain wildcards."), grecs_type_string, GRECS_DFLT, NULL, 0, _cb_env_unset }, { NULL } }; int string_to_syslog_priority (const char *key, int *pres) |