diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-02-11 14:05:10 -0500 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-02-20 23:30:50 -0500 |
commit | ccad1fd07ce4eb40a2fcf81cfb55d9b41fdcac48 (patch) | |
tree | 41b72c7747d8072e212ed58fd8b5c1e71c18d0b7 | |
parent | cb4499d0056a7c974d7d3695cc355c7e77edc938 (diff) |
Allow braceless variables to be expanded
(Only in environment.d files.)
We have only basic compatibility with shell syntax, but specifying variables
without using braces is probably more common, and I think a lot of people would
be surprised if this didn't work.
-rw-r--r-- | man/environment.d.xml | 4 | ||||
-rw-r--r-- | src/basic/env-util.c | 45 | ||||
-rw-r--r-- | src/basic/env-util.h | 1 | ||||
-rw-r--r-- | src/basic/fileio.c | 7 | ||||
-rw-r--r-- | src/test/test-env-util.c | 31 | ||||
-rw-r--r-- | src/test/test-fileio.c | 14 |
6 files changed, 91 insertions, 11 deletions
diff --git a/man/environment.d.xml b/man/environment.d.xml index 4f3e03825a..2302992fa5 100644 --- a/man/environment.d.xml +++ b/man/environment.d.xml @@ -78,7 +78,7 @@ <literal><replaceable>KEY</replaceable>=<replaceable>VALUE</replaceable></literal> environment variable assignments, separated by newlines. The right hand side of these assignments may reference previously defined environment variables, using the <literal>${OTHER_KEY}</literal> - format. No other elements of shell syntax are supported. + and <literal>$OTHER_KEY</literal> format. No other elements of shell syntax are supported. </para> <refsect2> @@ -91,7 +91,7 @@ </para> <programlisting> FOO_DEBUG=force-software-gl,log-verbose - PATH=/opt/foo/bin:${PATH} + PATH=/opt/foo/bin:$PATH LD_LIBRARY_PATH=/opt/foo/lib XDG_DATA_DIRS=/opt/foo/share:${XDG_DATA_DIRS} </programlisting> diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 8774a81531..f370854673 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -523,7 +523,8 @@ char *replace_env(const char *format, char **env, unsigned flags) { enum { WORD, CURLY, - VARIABLE + VARIABLE, + VARIABLE_RAW, } state = WORD; const char *e, *word = format; @@ -563,6 +564,18 @@ char *replace_env(const char *format, char **env, unsigned flags) { word = e+1; state = WORD; + + } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) { + k = strnappend(r, word, e-word-1); + if (!k) + return NULL; + + free(r); + r = k; + + word = e-1; + state = VARIABLE_RAW; + } else state = WORD; break; @@ -584,10 +597,38 @@ char *replace_env(const char *format, char **env, unsigned flags) { state = WORD; } break; + + case VARIABLE_RAW: + assert(flags & REPLACE_ENV_ALLOW_BRACELESS); + + if (!strchr(VALID_CHARS_ENV_NAME, *e)) { + const char *t; + + t = strv_env_get_n(env, word+1, e-word-1, flags); + + k = strappend(r, t); + if (!k) + return NULL; + + free(r); + r = k; + + word = e--; + state = WORD; + } + break; } } - return strnappend(r, word, e-word); + if (state == VARIABLE_RAW) { + const char *t; + + assert(flags & REPLACE_ENV_ALLOW_BRACELESS); + + t = strv_env_get_n(env, word+1, e-word-1, flags); + return strappend(r, t); + } else + return strnappend(r, word, e-word); } char **replace_env_argv(char **argv, char **env) { diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 4e83dcb43a..03bbc6af00 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -31,6 +31,7 @@ bool env_assignment_is_valid(const char *e); enum { REPLACE_ENV_USE_ENVIRONMENT = 1u, + REPLACE_ENV_ALLOW_BRACELESS = 2u, }; char *replace_env(const char *format, char **env, unsigned flags); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 49dd52bfd9..3c2dab1855 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -773,7 +773,8 @@ static int merge_env_file_push( assert(env); - expanded_value = replace_env(value, *env, REPLACE_ENV_USE_ENVIRONMENT); + expanded_value = replace_env(value, *env, + REPLACE_ENV_USE_ENVIRONMENT|REPLACE_ENV_ALLOW_BRACELESS); if (!expanded_value) return -ENOMEM; @@ -787,6 +788,10 @@ int merge_env_file( FILE *f, const char *fname) { + /* NOTE: this function supports braceful and braceless variable expansions, + * unlike other exported parsing functions. + */ + return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL); } diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index f44cb3d57b..77a5219d82 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -142,7 +142,32 @@ static void test_env_strv_get_n(void) { getenv("PATH"))); } -static void test_replace_env_arg(void) { +static void test_replace_env(bool braceless) { + const char *env[] = { + "FOO=BAR BAR", + "BAR=waldo", + NULL + }; + _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL; + unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless; + + t = replace_env("FOO=$FOO=${FOO}", (char**) env, flags); + assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR")); + + s = replace_env("BAR=$BAR=${BAR}", (char**) env, flags); + assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo")); + + q = replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags); + assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR=")); + + q = replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags); + assert_se(streq(q, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo")); + + p = replace_env("${BAR}$BAR$BAR", (char**) env, flags); + assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR")); +} + +static void test_replace_env_argv(void) { const char *env[] = { "FOO=BAR BAR", "BAR=waldo", @@ -256,7 +281,9 @@ int main(int argc, char *argv[]) { test_strv_env_set(); test_strv_env_merge(); test_env_strv_get_n(); - test_replace_env_arg(); + test_replace_env(false); + test_replace_env(true); + test_replace_env_argv(); test_env_clean(); test_env_name_is_valid(); test_env_value_is_valid(); diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 84f394a713..c204cbae22 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -226,7 +226,10 @@ static void test_merge_env_file(void) { "twelve=${one}2\n" "twentyone=2${one}\n" "one=2\n" - "twentytwo=2${one}\n", false); + "twentytwo=2${one}\n" + "xxx_minus_three=$xxx - 3\n" + "xxx=0x$one$one$one\n" + , false); assert(r >= 0); r = merge_env_file(&a, NULL, t); @@ -240,8 +243,9 @@ static void test_merge_env_file(void) { assert_se(streq(a[1], "twelve=12")); assert_se(streq(a[2], "twentyone=21")); assert_se(streq(a[3], "twentytwo=22")); - assert_se(a[4] == NULL); - + assert_se(streq(a[4], "xxx=0x222")); + assert_se(streq(a[5], "xxx_minus_three= - 3")); + assert_se(a[6] == NULL); r = merge_env_file(&a, NULL, t); assert_se(r >= 0); @@ -254,7 +258,9 @@ static void test_merge_env_file(void) { assert_se(streq(a[1], "twelve=12")); assert_se(streq(a[2], "twentyone=21")); assert_se(streq(a[3], "twentytwo=22")); - assert_se(a[4] == NULL); + assert_se(streq(a[4], "xxx=0x222")); + assert_se(streq(a[5], "xxx_minus_three=0x222 - 3")); + assert_se(a[6] == NULL); } static void test_executable_is_script(void) { |