#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 */
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
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;
+}