1 /* SPDX-License-Identifier: LGPL-2.1+ */
9 #include "alloc-util.h"
12 #include "extract-word.h"
14 #include "parse-util.h"
15 #include "string-util.h"
19 #define VALID_CHARS_ENV_NAME \
23 static bool env_name_is_valid_n(const char *e
, size_t n
) {
32 if (e
[0] >= '0' && e
[0] <= '9')
35 /* POSIX says the overall size of the environment block cannot
36 * be > ARG_MAX, an individual assignment hence cannot be
37 * either. Discounting the equal sign and trailing NUL this
38 * hence leaves ARG_MAX-2 as longest possible variable
40 if (n
> (size_t) sysconf(_SC_ARG_MAX
) - 2)
43 for (p
= e
; p
< e
+ n
; p
++)
44 if (!strchr(VALID_CHARS_ENV_NAME
, *p
))
50 bool env_name_is_valid(const char *e
) {
54 return env_name_is_valid_n(e
, strlen(e
));
57 bool env_value_is_valid(const char *e
) {
61 if (!utf8_is_valid(e
))
64 /* bash allows tabs and newlines in environment variables, and so
66 if (string_has_cc(e
, "\t\n"))
69 /* POSIX says the overall size of the environment block cannot
70 * be > ARG_MAX, an individual assignment hence cannot be
71 * either. Discounting the shortest possible variable name of
72 * length 1, the equal sign and trailing NUL this hence leaves
73 * ARG_MAX-3 as longest possible variable value. */
74 if (strlen(e
) > sc_arg_max() - 3)
80 bool env_assignment_is_valid(const char *e
) {
87 if (!env_name_is_valid_n(e
, eq
- e
))
90 if (!env_value_is_valid(eq
+ 1))
93 /* POSIX says the overall size of the environment block cannot
94 * be > ARG_MAX, hence the individual variable assignments
95 * cannot be either, but let's leave room for one trailing NUL
97 if (strlen(e
) > sc_arg_max() - 1)
103 bool strv_env_is_valid(char **e
) {
109 if (!env_assignment_is_valid(*p
))
112 /* Check if there are duplicate assignments */
113 k
= strcspn(*p
, "=");
114 STRV_FOREACH(q
, p
+ 1)
115 if (strneq(*p
, *q
, k
) && (*q
)[k
] == '=')
122 bool strv_env_name_is_valid(char **l
) {
126 if (!env_name_is_valid(*p
))
129 if (strv_contains(p
+ 1, *p
))
136 bool strv_env_name_or_assignment_is_valid(char **l
) {
140 if (!env_assignment_is_valid(*p
) && !env_name_is_valid(*p
))
143 if (strv_contains(p
+ 1, *p
))
150 static int env_append(char **r
, char ***k
, char **a
) {
158 /* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k'
159 * to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv.
161 * This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it.
163 * This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */
169 n
= strcspn(*a
, "=");
173 for (j
= r
; j
< *k
; j
++)
174 if (strneq(*j
, *a
, n
))
181 if (j
>= *k
) { /* Append to the end? */
186 free_and_replace(*j
, c
); /* Override existing item */
192 char **strv_env_merge(size_t n_lists
, ...) {
193 _cleanup_strv_free_
char **ret
= NULL
;
198 /* Merges an arbitrary number of environment sets */
200 va_start(ap
, n_lists
);
201 for (i
= 0; i
< n_lists
; i
++) {
202 l
= va_arg(ap
, char**);
207 ret
= new(char*, n
+1);
214 va_start(ap
, n_lists
);
215 for (i
= 0; i
< n_lists
; i
++) {
216 l
= va_arg(ap
, char**);
217 if (env_append(ret
, &k
, l
) < 0) {
224 return TAKE_PTR(ret
);
227 static bool env_match(const char *t
, const char *pattern
) {
231 /* pattern a matches string a
236 * a= does not match a
237 * a=b does not match a=
238 * a=b does not match a
239 * a=b does not match a=c */
241 if (streq(t
, pattern
))
244 if (!strchr(pattern
, '=')) {
245 size_t l
= strlen(pattern
);
247 return strneq(t
, pattern
, l
) && t
[l
] == '=';
253 static bool env_entry_has_name(const char *entry
, const char *name
) {
259 t
= startswith(entry
, name
);
266 char **strv_env_delete(char **x
, size_t n_lists
, ...) {
271 /* Deletes every entry from x that is mentioned in the other
283 va_start(ap
, n_lists
);
284 for (v
= 0; v
< n_lists
; v
++) {
287 l
= va_arg(ap
, char**);
289 if (env_match(*k
, *j
))
314 char **strv_env_unset(char **l
, const char *p
) {
323 /* Drops every occurrence of the env var setting p in the
324 * string list. Edits in-place. */
326 for (f
= t
= l
; *f
; f
++) {
328 if (env_match(*f
, p
)) {
340 char **strv_env_unset_many(char **l
, ...) {
346 /* Like strv_env_unset() but applies many at once. Edits in-place. */
348 for (f
= t
= l
; *f
; f
++) {
355 while ((p
= va_arg(ap
, const char*))) {
356 if (env_match(*f
, p
)) {
376 int strv_env_replace(char ***l
, char *p
) {
377 const char *t
, *name
;
383 /* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
384 * in-place. Does not copy p. p must be a valid key=value assignment.
391 name
= strndupa(p
, t
- p
);
394 if (env_entry_has_name(*f
, name
)) {
395 free_and_replace(*f
, p
);
396 strv_env_unset(f
+ 1, *f
);
400 /* We didn't find a match, we need to append p or create a new strv */
408 char **strv_env_set(char **x
, const char *p
) {
409 _cleanup_strv_free_
char **ret
= NULL
;
413 /* Overrides the env var setting of p, returns a new copy */
417 if (m
< n
) /* overflow? */
427 if (env_append(ret
, &k
, x
) < 0)
430 if (env_append(ret
, &k
, STRV_MAKE(p
)) < 0)
433 return TAKE_PTR(ret
);
436 char *strv_env_get_n(char **l
, const char *name
, size_t k
, unsigned flags
) {
444 STRV_FOREACH_BACKWARDS(i
, l
)
445 if (strneq(*i
, name
, k
) &&
449 if (flags
& REPLACE_ENV_USE_ENVIRONMENT
) {
452 t
= strndupa(name
, k
);
459 char *strv_env_get(char **l
, const char *name
) {
462 return strv_env_get_n(l
, name
, strlen(name
), 0);
465 char **strv_env_clean_with_callback(char **e
, void (*invalid_callback
)(const char *p
, void *userdata
), void *userdata
) {
471 bool duplicate
= false;
473 if (!env_assignment_is_valid(*p
)) {
474 if (invalid_callback
)
475 invalid_callback(*p
, userdata
);
480 n
= strcspn(*p
, "=");
481 STRV_FOREACH(q
, p
+ 1)
482 if (strneq(*p
, *q
, n
) && (*q
)[n
] == '=') {
501 char *replace_env_n(const char *format
, size_t n
, char **env
, unsigned flags
) {
512 const char *e
, *word
= format
, *test_value
;
514 _cleanup_free_
char *r
= NULL
;
520 for (e
= format
, i
= 0; *e
&& i
< n
; e
++, i
++)
530 k
= strnappend(r
, word
, e
-word
-1);
534 free_and_replace(r
, k
);
539 } else if (*e
== '$') {
540 k
= strnappend(r
, word
, e
-word
);
544 free_and_replace(r
, k
);
549 } else if (flags
& REPLACE_ENV_ALLOW_BRACELESS
&& strchr(VALID_CHARS_ENV_NAME
, *e
)) {
550 k
= strnappend(r
, word
, e
-word
-1);
554 free_and_replace(r
, k
);
557 state
= VARIABLE_RAW
;
567 t
= strv_env_get_n(env
, word
+2, e
-word
-2, flags
);
573 free_and_replace(r
, k
);
577 } else if (*e
== ':') {
578 if (!(flags
& REPLACE_ENV_ALLOW_EXTENDED
))
579 /* Treat this as unsupported syntax, i.e. do no replacement */
590 state
= DEFAULT_VALUE
;
592 state
= ALTERNATE_VALUE
;
601 case DEFAULT_VALUE
: /* fall through */
602 case ALTERNATE_VALUE
:
603 assert(flags
& REPLACE_ENV_ALLOW_EXTENDED
);
616 _cleanup_free_
char *v
= NULL
;
618 t
= strv_env_get_n(env
, word
+2, len
, flags
);
620 if (t
&& state
== ALTERNATE_VALUE
)
621 t
= v
= replace_env_n(test_value
, e
-test_value
, env
, flags
);
622 else if (!t
&& state
== DEFAULT_VALUE
)
623 t
= v
= replace_env_n(test_value
, e
-test_value
, env
, flags
);
629 free_and_replace(r
, k
);
637 assert(flags
& REPLACE_ENV_ALLOW_BRACELESS
);
639 if (!strchr(VALID_CHARS_ENV_NAME
, *e
)) {
642 t
= strv_env_get_n(env
, word
+1, e
-word
-1, flags
);
648 free_and_replace(r
, k
);
657 if (state
== VARIABLE_RAW
) {
660 assert(flags
& REPLACE_ENV_ALLOW_BRACELESS
);
662 t
= strv_env_get_n(env
, word
+1, e
-word
-1, flags
);
663 return strjoin(r
, t
);
665 return strnappend(r
, word
, e
-word
);
668 char **replace_env_argv(char **argv
, char **env
) {
672 l
= strv_length(argv
);
674 ret
= new(char*, l
+1);
678 STRV_FOREACH(i
, argv
) {
680 /* If $FOO appears as single word, replace it by the split up variable */
681 if ((*i
)[0] == '$' && !IN_SET((*i
)[1], '{', '$')) {
683 char **w
, **m
= NULL
;
686 e
= strv_env_get(env
, *i
+1);
690 r
= strv_split_full(&m
, e
, WHITESPACE
, EXTRACT_RELAX
|EXTRACT_UNQUOTE
);
702 w
= reallocarray(ret
, l
+ 1, sizeof(char *));
712 memcpy(ret
+ k
, m
, q
* sizeof(char*));
720 /* If ${FOO} appears as part of a word, replace it by the variable as-is */
721 ret
[k
] = replace_env(*i
, env
, 0);
733 int getenv_bool(const char *p
) {
740 return parse_boolean(e
);
743 int getenv_bool_secure(const char *p
) {
746 e
= secure_getenv(p
);
750 return parse_boolean(e
);