From 2ec589ec1968001fcdf0cee66ae69e8b09c1e9ba Mon Sep 17 00:00:00 2001 From: Joel Rosdahl Date: Sun, 3 Apr 2011 21:51:10 +0200 Subject: [PATCH] Add subst_env_in_string function --- ccache.h | 1 + test/test_util.c | 38 +++++++++++++++++++++++ util.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/ccache.h b/ccache.h index 64baf7348..59c05f7bb 100644 --- a/ccache.h +++ b/ccache.h @@ -152,6 +152,7 @@ char *x_readlink(const char *path); #endif bool read_file(const char *path, size_t size_hint, char **data, size_t *size); char *read_text_file(const char *path, size_t size_hint); +char *subst_env_in_string(const char *str, char **errmsg); /* ------------------------------------------------------------------------- */ /* stats.c */ diff --git a/test/test_util.c b/test/test_util.c index a92d55c2c..435720558 100644 --- a/test/test_util.c +++ b/test/test_util.c @@ -56,4 +56,42 @@ TEST(format_hash_as_string) format_hash_as_string(hash, 12345)); } +TEST(subst_env_in_string) +{ + char *errmsg; + const char *shell = getenv("SHELL"); + + errmsg = ""; + CHECK_STR_EQ_FREE2(shell, + subst_env_in_string("$SHELL", &errmsg)); + CHECK(!errmsg); + + errmsg = ""; + CHECK_STR_EQ_FREE2("$", + subst_env_in_string("$", &errmsg)); + CHECK(!errmsg); + + errmsg = ""; + CHECK_STR_EQ_FREE12(format("%s %s:%s", shell, shell, shell), + subst_env_in_string("$SHELL $SHELL:$SHELL", &errmsg)); + CHECK(!errmsg); + + errmsg = ""; + CHECK_STR_EQ_FREE12(format("x%s", shell), + subst_env_in_string("x$SHELL", &errmsg)); + CHECK(!errmsg); + + errmsg = ""; + CHECK_STR_EQ_FREE12(format("%sx", shell), + subst_env_in_string("${SHELL}x", &errmsg)); + CHECK(!errmsg); + + CHECK(!subst_env_in_string("$surelydoesntexist", &errmsg)); + CHECK_STR_EQ_FREE2("environment variable \"surelydoesntexist\" not set", + errmsg); + + CHECK(!subst_env_in_string("${SHELL", &errmsg)); + CHECK_STR_EQ_FREE2("syntax error: missing '}' after \"SHELL\"", errmsg); +} + TEST_SUITE_END diff --git a/util.c b/util.c index d64cd2320..1b7c950ce 100644 --- a/util.c +++ b/util.c @@ -1270,3 +1270,83 @@ read_text_file(const char *path, size_t size_hint) return NULL; } } + +static bool +expand_variable(const char **str, char **result, char **errmsg) +{ + bool curly; + const char *p, *q; + char *name; + const char *value; + + assert(**str == '$'); + p = *str + 1; + if (*p == '{') { + curly = true; + ++p; + } else { + curly = false; + } + q = p; + while (isalnum(*q) || *q == '_') { + ++q; + } + if (curly) { + if (*q != '}') { + *errmsg = format("syntax error: missing '}' after \"%s\"", p); + return NULL; + } + } + + if (q == p) { + /* Special case: don't consider a single $ the start of a variable. */ + x_asprintf2(result, "%s$", *result); + return true; + } + + name = x_strndup(p, q - p); + value = getenv(name); + if (!value) { + *errmsg = format("environment variable \"%s\" not set", name); + return false; + } + x_asprintf2(result, "%s%s", *result, value); + if (!curly) { + --q; + } + *str = q; + return true; +} + +/* + * Substitute all instances of $VAR or ${VAR}, where VAR is an environment + * variable, in a string. Caller frees. If one of the environment variables + * doesn't exist, NULL will be returned and *errmsg will be an appropriate + * error message (caller frees). + */ +char * +subst_env_in_string(const char *str, char **errmsg) +{ + const char *p; /* Interval start. */ + const char *q; /* Interval end. */ + char *result; + + assert(errmsg); + *errmsg = NULL; + + result = x_strdup(""); + p = str; + q = str; + for (q = str; *q; ++q) { + if (*q == '$') { + x_asprintf2(&result, "%s%.*s", result, (int)(q - p), p); + if (!expand_variable(&q, &result, errmsg)) { + free(result); + return NULL; + } + p = q + 1; + } + } + x_asprintf2(&result, "%s%.*s", result, (int)(q - p), p); + return result; +} -- 2.47.3