Skip to content

Commit 37f3ffc

Browse files
halflinekeszybz
authored andcommitted
basic: add new merge_env_file function
merge_env_file is a new function, that's like load_env_file, but takes a pre-existing environment as an input argument. New environment entries are merged. Variable expansion is performed. Falling back to the process environment is supported (when a flag is set). Alternatively this could be implemented as passing an additional fallback environment array, but later on we're adding another flag to allow braceless expansion, and the two flags can be combined in one arg, so there's less stuff to pass around.
1 parent d8ad241 commit 37f3ffc

File tree

6 files changed

+131
-7
lines changed

6 files changed

+131
-7
lines changed

src/basic/env-util.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ char **strv_env_set(char **x, const char *p) {
454454
return NULL;
455455
}
456456

457-
char *strv_env_get_n(char **l, const char *name, size_t k) {
457+
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
458458
char **i;
459459

460460
assert(name);
@@ -467,13 +467,20 @@ char *strv_env_get_n(char **l, const char *name, size_t k) {
467467
(*i)[k] == '=')
468468
return *i + k + 1;
469469

470+
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
471+
const char *t;
472+
473+
t = strndupa(name, k);
474+
return getenv(t);
475+
};
476+
470477
return NULL;
471478
}
472479

473480
char *strv_env_get(char **l, const char *name) {
474481
assert(name);
475482

476-
return strv_env_get_n(l, name, strlen(name));
483+
return strv_env_get_n(l, name, strlen(name), 0);
477484
}
478485

479486
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
@@ -512,7 +519,7 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
512519
return e;
513520
}
514521

515-
char *replace_env(const char *format, char **env) {
522+
char *replace_env(const char *format, char **env, unsigned flags) {
516523
enum {
517524
WORD,
518525
CURLY,
@@ -563,7 +570,7 @@ char *replace_env(const char *format, char **env) {
563570
if (*e == '}') {
564571
const char *t;
565572

566-
t = strv_env_get_n(env, word+2, e-word-2);
573+
t = strv_env_get_n(env, word+2, e-word-2, flags);
567574

568575
k = strappend(r, t);
569576
if (!k)
@@ -643,7 +650,7 @@ char **replace_env_argv(char **argv, char **env) {
643650
}
644651

645652
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
646-
ret[k] = replace_env(*i, env);
653+
ret[k] = replace_env(*i, env, 0);
647654
if (!ret[k]) {
648655
strv_free(ret);
649656
return NULL;

src/basic/env-util.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ bool env_name_is_valid(const char *e);
2929
bool env_value_is_valid(const char *e);
3030
bool env_assignment_is_valid(const char *e);
3131

32-
char *replace_env(const char *format, char **env);
32+
enum {
33+
REPLACE_ENV_USE_ENVIRONMENT = 1u,
34+
};
35+
36+
char *replace_env(const char *format, char **env, unsigned flags);
3337
char **replace_env_argv(char **argv, char **env);
3438

3539
bool strv_env_is_valid(char **e);
@@ -47,7 +51,7 @@ char **strv_env_unset(char **l, const char *p); /* In place ... */
4751
char **strv_env_unset_many(char **l, ...) _sentinel_;
4852
int strv_env_replace(char ***l, char *p); /* In place ... */
4953

50-
char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
54+
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
5155
char *strv_env_get(char **x, const char *n) _pure_;
5256

5357
int getenv_bool(const char *p);

src/basic/fileio.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,34 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
762762
return 0;
763763
}
764764

765+
static int merge_env_file_push(
766+
const char *filename, unsigned line,
767+
const char *key, char *value,
768+
void *userdata,
769+
int *n_pushed) {
770+
771+
char ***env = userdata;
772+
char *expanded_value;
773+
774+
assert(env);
775+
776+
expanded_value = replace_env(value, *env, REPLACE_ENV_USE_ENVIRONMENT);
777+
if (!expanded_value)
778+
return -ENOMEM;
779+
780+
free_and_replace(value, expanded_value);
781+
782+
return load_env_file_push(filename, line, key, value, env, n_pushed);
783+
}
784+
785+
int merge_env_file(
786+
char ***env,
787+
FILE *f,
788+
const char *fname) {
789+
790+
return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
791+
}
792+
765793
static void write_env_var(FILE *f, const char *v) {
766794
const char *p;
767795

src/basic/fileio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
4848
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
4949
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
5050

51+
int merge_env_file(char ***env, FILE *f, const char *fname);
52+
5153
int write_env_file(const char *fname, char **l);
5254

5355
int executable_is_script(const char *path, char **interpreter);

src/test/test-env-util.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,36 @@ static void test_strv_env_merge(void) {
112112
assert_se(strv_length(r) == 5);
113113
}
114114

115+
static void test_env_strv_get_n(void) {
116+
const char *_env[] = {
117+
"FOO=NO NO NO",
118+
"FOO=BAR BAR",
119+
"BAR=waldo",
120+
"PATH=unset",
121+
NULL
122+
};
123+
char **env = (char**) _env;
124+
125+
assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR"));
126+
assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
127+
assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR"));
128+
assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR"));
129+
130+
assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset"));
131+
assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset"));
132+
assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
133+
assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset"));
134+
135+
env[3] = NULL; /* kill our $PATH */
136+
137+
assert_se(!strv_env_get_n(env, "PATH__", 4, 0));
138+
assert_se(!strv_env_get_n(env, "PATH", 4, 0));
139+
assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT),
140+
getenv("PATH")));
141+
assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT),
142+
getenv("PATH")));
143+
}
144+
115145
static void test_replace_env_arg(void) {
116146
const char *env[] = {
117147
"FOO=BAR BAR",
@@ -225,6 +255,7 @@ int main(int argc, char *argv[]) {
225255
test_strv_env_unset();
226256
test_strv_env_set();
227257
test_strv_env_merge();
258+
test_env_strv_get_n();
228259
test_replace_env_arg();
229260
test_env_clean();
230261
test_env_name_is_valid();

src/test/test-fileio.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,56 @@ static void test_parse_multiline_env_file(void) {
206206
unlink(p);
207207
}
208208

209+
static void test_merge_env_file(void) {
210+
char t[] = "/tmp/test-fileio-XXXXXX";
211+
int fd, r;
212+
FILE *f;
213+
_cleanup_strv_free_ char **a = NULL;
214+
char **i;
215+
216+
fd = mkostemp_safe(t);
217+
assert_se(fd >= 0);
218+
219+
log_info("/* %s (%s) */", __func__, t);
220+
221+
f = fdopen(fd, "w");
222+
assert_se(f);
223+
224+
r = write_string_stream(f,
225+
"one=1 \n"
226+
"twelve=${one}2\n"
227+
"twentyone=2${one}\n"
228+
"one=2\n"
229+
"twentytwo=2${one}\n", false);
230+
assert(r >= 0);
231+
232+
r = merge_env_file(&a, NULL, t);
233+
assert_se(r >= 0);
234+
strv_sort(a);
235+
236+
STRV_FOREACH(i, a)
237+
log_info("Got: <%s>", *i);
238+
239+
assert_se(streq(a[0], "one=2"));
240+
assert_se(streq(a[1], "twelve=12"));
241+
assert_se(streq(a[2], "twentyone=21"));
242+
assert_se(streq(a[3], "twentytwo=22"));
243+
assert_se(a[4] == NULL);
244+
245+
246+
r = merge_env_file(&a, NULL, t);
247+
assert_se(r >= 0);
248+
strv_sort(a);
249+
250+
STRV_FOREACH(i, a)
251+
log_info("Got2: <%s>", *i);
252+
253+
assert_se(streq(a[0], "one=2"));
254+
assert_se(streq(a[1], "twelve=12"));
255+
assert_se(streq(a[2], "twentyone=21"));
256+
assert_se(streq(a[3], "twentytwo=22"));
257+
assert_se(a[4] == NULL);
258+
}
209259

210260
static void test_executable_is_script(void) {
211261
char t[] = "/tmp/test-executable-XXXXXX";
@@ -557,11 +607,13 @@ static void test_tempfn(void) {
557607
}
558608

559609
int main(int argc, char *argv[]) {
610+
log_set_max_level(LOG_DEBUG);
560611
log_parse_environment();
561612
log_open();
562613

563614
test_parse_env_file();
564615
test_parse_multiline_env_file();
616+
test_merge_env_file();
565617
test_executable_is_script();
566618
test_status_field();
567619
test_capeff();

0 commit comments

Comments
 (0)