systemd: reorganize source files
This is a direct dump from systemd git. ====== SYSTEMD_DIR=../systemd COMMIT=8eab766804ef4fa21d26c00fd0baab3f1a47bb5c ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) git ls-files -z :/src/systemd/src/ \ :/shared/systemd/src/ \ :/shared/nm-utils/unaligned.h | \ xargs -0 rm -f nm_copy_sd_shared() { mkdir -p "./shared/systemd/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./shared/systemd/$1" } nm_copy_sd_core() { mkdir -p "./src/systemd/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/systemd/$1" } nm_copy_sd_nmutils() { mkdir -p "./shared/nm-utils/" cp "$SYSTEMD_DIR/$1" "./shared/nm-utils/${1##*/}" } nm_copy_sd_core "src/libsystemd-network/arp-util.c" nm_copy_sd_core "src/libsystemd-network/arp-util.h" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h" nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c" nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h" nm_copy_sd_core "src/libsystemd-network/lldp-internal.h" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h" nm_copy_sd_core "src/libsystemd-network/lldp-network.c" nm_copy_sd_core "src/libsystemd-network/lldp-network.h" nm_copy_sd_core "src/libsystemd-network/network-internal.c" nm_copy_sd_core "src/libsystemd-network/network-internal.h" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4acd.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c" nm_copy_sd_core "src/libsystemd-network/sd-lldp.c" nm_copy_sd_core "src/libsystemd/sd-event/event-source.h" nm_copy_sd_core "src/libsystemd/sd-event/event-util.c" nm_copy_sd_core "src/libsystemd/sd-event/event-util.h" nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h" nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c" nm_copy_sd_core "src/shared/dns-domain.c" nm_copy_sd_core "src/shared/dns-domain.h" nm_copy_sd_core "src/systemd/_sd-common.h" nm_copy_sd_core "src/systemd/sd-dhcp-client.h" nm_copy_sd_core "src/systemd/sd-dhcp-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp6-client.h" nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h" nm_copy_sd_core "src/systemd/sd-event.h" nm_copy_sd_core "src/systemd/sd-id128.h" nm_copy_sd_core "src/systemd/sd-ipv4acd.h" nm_copy_sd_core "src/systemd/sd-ipv4ll.h" nm_copy_sd_core "src/systemd/sd-lldp.h" nm_copy_sd_core "src/systemd/sd-ndisc.h" nm_copy_sd_nmutils "src/basic/unaligned.h" nm_copy_sd_shared "src/basic/alloc-util.c" nm_copy_sd_shared "src/basic/alloc-util.h" nm_copy_sd_shared "src/basic/async.h" nm_copy_sd_shared "src/basic/env-file.c" nm_copy_sd_shared "src/basic/env-file.h" nm_copy_sd_shared "src/basic/env-util.c" nm_copy_sd_shared "src/basic/env-util.h" nm_copy_sd_shared "src/basic/escape.c" nm_copy_sd_shared "src/basic/escape.h" nm_copy_sd_shared "src/basic/ether-addr-util.c" nm_copy_sd_shared "src/basic/ether-addr-util.h" nm_copy_sd_shared "src/basic/extract-word.c" nm_copy_sd_shared "src/basic/extract-word.h" nm_copy_sd_shared "src/basic/fd-util.c" nm_copy_sd_shared "src/basic/fd-util.h" nm_copy_sd_shared "src/basic/fileio.c" nm_copy_sd_shared "src/basic/fileio.h" nm_copy_sd_shared "src/basic/fs-util.c" nm_copy_sd_shared "src/basic/fs-util.h" nm_copy_sd_shared "src/basic/hash-funcs.c" nm_copy_sd_shared "src/basic/hash-funcs.h" nm_copy_sd_shared "src/basic/hashmap.c" nm_copy_sd_shared "src/basic/hashmap.h" nm_copy_sd_shared "src/basic/hexdecoct.c" nm_copy_sd_shared "src/basic/hexdecoct.h" nm_copy_sd_shared "src/basic/hostname-util.c" nm_copy_sd_shared "src/basic/hostname-util.h" nm_copy_sd_shared "src/basic/in-addr-util.c" nm_copy_sd_shared "src/basic/in-addr-util.h" nm_copy_sd_shared "src/basic/io-util.c" nm_copy_sd_shared "src/basic/io-util.h" nm_copy_sd_shared "src/basic/list.h" nm_copy_sd_shared "src/basic/log.h" nm_copy_sd_shared "src/basic/macro.h" nm_copy_sd_shared "src/basic/mempool.c" nm_copy_sd_shared "src/basic/mempool.h" nm_copy_sd_shared "src/basic/missing_type.h" nm_copy_sd_shared "src/basic/parse-util.c" nm_copy_sd_shared "src/basic/parse-util.h" nm_copy_sd_shared "src/basic/path-util.c" nm_copy_sd_shared "src/basic/path-util.h" nm_copy_sd_shared "src/basic/prioq.c" nm_copy_sd_shared "src/basic/prioq.h" nm_copy_sd_shared "src/basic/process-util.c" nm_copy_sd_shared "src/basic/process-util.h" nm_copy_sd_shared "src/basic/random-util.c" nm_copy_sd_shared "src/basic/random-util.h" nm_copy_sd_shared "src/basic/refcnt.h" nm_copy_sd_shared "src/basic/set.h" nm_copy_sd_shared "src/basic/signal-util.h" nm_copy_sd_shared "src/basic/siphash24.h" nm_copy_sd_shared "src/basic/socket-util.c" nm_copy_sd_shared "src/basic/socket-util.h" nm_copy_sd_shared "src/basic/sparse-endian.h" nm_copy_sd_shared "src/basic/stat-util.c" nm_copy_sd_shared "src/basic/stat-util.h" nm_copy_sd_shared "src/basic/stdio-util.h" nm_copy_sd_shared "src/basic/string-table.c" nm_copy_sd_shared "src/basic/string-table.h" nm_copy_sd_shared "src/basic/string-util.c" nm_copy_sd_shared "src/basic/string-util.h" nm_copy_sd_shared "src/basic/strv.c" nm_copy_sd_shared "src/basic/strv.h" nm_copy_sd_shared "src/basic/time-util.c" nm_copy_sd_shared "src/basic/time-util.h" nm_copy_sd_shared "src/basic/tmpfile-util.c" nm_copy_sd_shared "src/basic/tmpfile-util.h" nm_copy_sd_shared "src/basic/umask-util.h" nm_copy_sd_shared "src/basic/utf8.c" nm_copy_sd_shared "src/basic/utf8.h" nm_copy_sd_shared "src/basic/util.c" nm_copy_sd_shared "src/basic/util.h"
This commit is contained in:
754
shared/systemd/src/basic/env-util.c
Normal file
754
shared/systemd/src/basic/env-util.c
Normal file
@@ -0,0 +1,754 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "extract-word.h"
|
||||
#include "macro.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "utf8.h"
|
||||
|
||||
#define VALID_CHARS_ENV_NAME \
|
||||
DIGITS LETTERS \
|
||||
"_"
|
||||
|
||||
static bool env_name_is_valid_n(const char *e, size_t n) {
|
||||
const char *p;
|
||||
|
||||
if (!e)
|
||||
return false;
|
||||
|
||||
if (n <= 0)
|
||||
return false;
|
||||
|
||||
if (e[0] >= '0' && e[0] <= '9')
|
||||
return false;
|
||||
|
||||
/* POSIX says the overall size of the environment block cannot
|
||||
* be > ARG_MAX, an individual assignment hence cannot be
|
||||
* either. Discounting the equal sign and trailing NUL this
|
||||
* hence leaves ARG_MAX-2 as longest possible variable
|
||||
* name. */
|
||||
if (n > (size_t) sysconf(_SC_ARG_MAX) - 2)
|
||||
return false;
|
||||
|
||||
for (p = e; p < e + n; p++)
|
||||
if (!strchr(VALID_CHARS_ENV_NAME, *p))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool env_name_is_valid(const char *e) {
|
||||
if (!e)
|
||||
return false;
|
||||
|
||||
return env_name_is_valid_n(e, strlen(e));
|
||||
}
|
||||
|
||||
bool env_value_is_valid(const char *e) {
|
||||
if (!e)
|
||||
return false;
|
||||
|
||||
if (!utf8_is_valid(e))
|
||||
return false;
|
||||
|
||||
/* bash allows tabs and newlines in environment variables, and so
|
||||
* should we */
|
||||
if (string_has_cc(e, "\t\n"))
|
||||
return false;
|
||||
|
||||
/* POSIX says the overall size of the environment block cannot
|
||||
* be > ARG_MAX, an individual assignment hence cannot be
|
||||
* either. Discounting the shortest possible variable name of
|
||||
* length 1, the equal sign and trailing NUL this hence leaves
|
||||
* ARG_MAX-3 as longest possible variable value. */
|
||||
if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 3)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool env_assignment_is_valid(const char *e) {
|
||||
const char *eq;
|
||||
|
||||
eq = strchr(e, '=');
|
||||
if (!eq)
|
||||
return false;
|
||||
|
||||
if (!env_name_is_valid_n(e, eq - e))
|
||||
return false;
|
||||
|
||||
if (!env_value_is_valid(eq + 1))
|
||||
return false;
|
||||
|
||||
/* POSIX says the overall size of the environment block cannot
|
||||
* be > ARG_MAX, hence the individual variable assignments
|
||||
* cannot be either, but let's leave room for one trailing NUL
|
||||
* byte. */
|
||||
if (strlen(e) > (size_t) sysconf(_SC_ARG_MAX) - 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strv_env_is_valid(char **e) {
|
||||
char **p, **q;
|
||||
|
||||
STRV_FOREACH(p, e) {
|
||||
size_t k;
|
||||
|
||||
if (!env_assignment_is_valid(*p))
|
||||
return false;
|
||||
|
||||
/* Check if there are duplicate assignments */
|
||||
k = strcspn(*p, "=");
|
||||
STRV_FOREACH(q, p + 1)
|
||||
if (strneq(*p, *q, k) && (*q)[k] == '=')
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strv_env_name_is_valid(char **l) {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, l) {
|
||||
if (!env_name_is_valid(*p))
|
||||
return false;
|
||||
|
||||
if (strv_contains(p + 1, *p))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strv_env_name_or_assignment_is_valid(char **l) {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, l) {
|
||||
if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
|
||||
return false;
|
||||
|
||||
if (strv_contains(p + 1, *p))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int env_append(char **r, char ***k, char **a) {
|
||||
assert(r);
|
||||
assert(k);
|
||||
assert(*k >= r);
|
||||
|
||||
if (!a)
|
||||
return 0;
|
||||
|
||||
/* Expects the following arguments: 'r' shall point to the beginning of an strv we are going to append to, 'k'
|
||||
* to a pointer pointing to the NULL entry at the end of the same array. 'a' shall point to another strv.
|
||||
*
|
||||
* This call adds every entry of 'a' to 'r', either overriding an existing matching entry, or appending to it.
|
||||
*
|
||||
* This call assumes 'r' has enough pre-allocated space to grow by all of 'a''s items. */
|
||||
|
||||
for (; *a; a++) {
|
||||
char **j, *c;
|
||||
size_t n;
|
||||
|
||||
n = strcspn(*a, "=");
|
||||
if ((*a)[n] == '=')
|
||||
n++;
|
||||
|
||||
for (j = r; j < *k; j++)
|
||||
if (strneq(*j, *a, n))
|
||||
break;
|
||||
|
||||
c = strdup(*a);
|
||||
if (!c)
|
||||
return -ENOMEM;
|
||||
|
||||
if (j >= *k) { /* Append to the end? */
|
||||
(*k)[0] = c;
|
||||
(*k)[1] = NULL;
|
||||
(*k)++;
|
||||
} else
|
||||
free_and_replace(*j, c); /* Override existing item */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **strv_env_merge(size_t n_lists, ...) {
|
||||
_cleanup_strv_free_ char **ret = NULL;
|
||||
size_t n = 0, i;
|
||||
char **l, **k;
|
||||
va_list ap;
|
||||
|
||||
/* Merges an arbitrary number of environment sets */
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (i = 0; i < n_lists; i++) {
|
||||
l = va_arg(ap, char**);
|
||||
n += strv_length(l);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
ret = new(char*, n+1);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
*ret = NULL;
|
||||
k = ret;
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (i = 0; i < n_lists; i++) {
|
||||
l = va_arg(ap, char**);
|
||||
if (env_append(ret, &k, l) < 0) {
|
||||
va_end(ap);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return TAKE_PTR(ret);
|
||||
}
|
||||
|
||||
static bool env_match(const char *t, const char *pattern) {
|
||||
assert(t);
|
||||
assert(pattern);
|
||||
|
||||
/* pattern a matches string a
|
||||
* a matches a=
|
||||
* a matches a=b
|
||||
* a= matches a=
|
||||
* a=b matches a=b
|
||||
* a= does not match a
|
||||
* a=b does not match a=
|
||||
* a=b does not match a
|
||||
* a=b does not match a=c */
|
||||
|
||||
if (streq(t, pattern))
|
||||
return true;
|
||||
|
||||
if (!strchr(pattern, '=')) {
|
||||
size_t l = strlen(pattern);
|
||||
|
||||
return strneq(t, pattern, l) && t[l] == '=';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool env_entry_has_name(const char *entry, const char *name) {
|
||||
const char *t;
|
||||
|
||||
assert(entry);
|
||||
assert(name);
|
||||
|
||||
t = startswith(entry, name);
|
||||
if (!t)
|
||||
return false;
|
||||
|
||||
return *t == '=';
|
||||
}
|
||||
|
||||
char **strv_env_delete(char **x, size_t n_lists, ...) {
|
||||
size_t n, i = 0;
|
||||
char **k, **r;
|
||||
va_list ap;
|
||||
|
||||
/* Deletes every entry from x that is mentioned in the other
|
||||
* string lists */
|
||||
|
||||
n = strv_length(x);
|
||||
|
||||
r = new(char*, n+1);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
STRV_FOREACH(k, x) {
|
||||
size_t v;
|
||||
|
||||
va_start(ap, n_lists);
|
||||
for (v = 0; v < n_lists; v++) {
|
||||
char **l, **j;
|
||||
|
||||
l = va_arg(ap, char**);
|
||||
STRV_FOREACH(j, l)
|
||||
if (env_match(*k, *j))
|
||||
goto skip;
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
r[i] = strdup(*k);
|
||||
if (!r[i]) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
|
||||
skip:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
r[i] = NULL;
|
||||
|
||||
assert(i <= n);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
char **strv_env_unset(char **l, const char *p) {
|
||||
|
||||
char **f, **t;
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* Drops every occurrence of the env var setting p in the
|
||||
* string list. Edits in-place. */
|
||||
|
||||
for (f = t = l; *f; f++) {
|
||||
|
||||
if (env_match(*f, p)) {
|
||||
free(*f);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(t++) = *f;
|
||||
}
|
||||
|
||||
*t = NULL;
|
||||
return l;
|
||||
}
|
||||
|
||||
char **strv_env_unset_many(char **l, ...) {
|
||||
|
||||
char **f, **t;
|
||||
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
/* Like strv_env_unset() but applies many at once. Edits in-place. */
|
||||
|
||||
for (f = t = l; *f; f++) {
|
||||
bool found = false;
|
||||
const char *p;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, l);
|
||||
|
||||
while ((p = va_arg(ap, const char*))) {
|
||||
if (env_match(*f, p)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
if (found) {
|
||||
free(*f);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(t++) = *f;
|
||||
}
|
||||
|
||||
*t = NULL;
|
||||
return l;
|
||||
}
|
||||
|
||||
int strv_env_replace(char ***l, char *p) {
|
||||
const char *t, *name;
|
||||
char **f;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* Replace first occurrence of the env var or add a new one in the string list. Drop other occurrences. Edits
|
||||
* in-place. Does not copy p. p must be a valid key=value assignment.
|
||||
*/
|
||||
|
||||
t = strchr(p, '=');
|
||||
if (!t)
|
||||
return -EINVAL;
|
||||
|
||||
name = strndupa(p, t - p);
|
||||
|
||||
STRV_FOREACH(f, *l)
|
||||
if (env_entry_has_name(*f, name)) {
|
||||
free_and_replace(*f, p);
|
||||
strv_env_unset(f + 1, *f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We didn't find a match, we need to append p or create a new strv */
|
||||
r = strv_push(l, p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char **strv_env_set(char **x, const char *p) {
|
||||
|
||||
_cleanup_strv_free_ char **ret = NULL;
|
||||
size_t n, m;
|
||||
char **k;
|
||||
|
||||
/* Overrides the env var setting of p, returns a new copy */
|
||||
|
||||
n = strv_length(x);
|
||||
m = n + 2;
|
||||
if (m < n) /* overflow? */
|
||||
return NULL;
|
||||
|
||||
ret = new(char*, m);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
*ret = NULL;
|
||||
k = ret;
|
||||
|
||||
if (env_append(ret, &k, x) < 0)
|
||||
return NULL;
|
||||
|
||||
if (env_append(ret, &k, STRV_MAKE(p)) < 0)
|
||||
return NULL;
|
||||
|
||||
return TAKE_PTR(ret);
|
||||
}
|
||||
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
|
||||
char **i;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (k <= 0)
|
||||
return NULL;
|
||||
|
||||
STRV_FOREACH_BACKWARDS(i, l)
|
||||
if (strneq(*i, name, k) &&
|
||||
(*i)[k] == '=')
|
||||
return *i + k + 1;
|
||||
|
||||
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
|
||||
const char *t;
|
||||
|
||||
t = strndupa(name, k);
|
||||
return getenv(t);
|
||||
};
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *strv_env_get(char **l, const char *name) {
|
||||
assert(name);
|
||||
|
||||
return strv_env_get_n(l, name, strlen(name), 0);
|
||||
}
|
||||
|
||||
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
|
||||
char **p, **q;
|
||||
int k = 0;
|
||||
|
||||
STRV_FOREACH(p, e) {
|
||||
size_t n;
|
||||
bool duplicate = false;
|
||||
|
||||
if (!env_assignment_is_valid(*p)) {
|
||||
if (invalid_callback)
|
||||
invalid_callback(*p, userdata);
|
||||
free(*p);
|
||||
continue;
|
||||
}
|
||||
|
||||
n = strcspn(*p, "=");
|
||||
STRV_FOREACH(q, p + 1)
|
||||
if (strneq(*p, *q, n) && (*q)[n] == '=') {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (duplicate) {
|
||||
free(*p);
|
||||
continue;
|
||||
}
|
||||
|
||||
e[k++] = *p;
|
||||
}
|
||||
|
||||
if (e)
|
||||
e[k] = NULL;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
||||
enum {
|
||||
WORD,
|
||||
CURLY,
|
||||
VARIABLE,
|
||||
VARIABLE_RAW,
|
||||
TEST,
|
||||
DEFAULT_VALUE,
|
||||
ALTERNATE_VALUE,
|
||||
} state = WORD;
|
||||
|
||||
const char *e, *word = format, *test_value;
|
||||
char *k;
|
||||
_cleanup_free_ char *r = NULL;
|
||||
size_t i, len;
|
||||
int nest = 0;
|
||||
|
||||
assert(format);
|
||||
|
||||
for (e = format, i = 0; *e && i < n; e ++, i ++)
|
||||
switch (state) {
|
||||
|
||||
case WORD:
|
||||
if (*e == '$')
|
||||
state = CURLY;
|
||||
break;
|
||||
|
||||
case CURLY:
|
||||
if (*e == '{') {
|
||||
k = strnappend(r, word, e-word-1);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
word = e-1;
|
||||
state = VARIABLE;
|
||||
nest++;
|
||||
} else if (*e == '$') {
|
||||
k = strnappend(r, word, e-word);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
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_and_replace(r, k);
|
||||
|
||||
word = e-1;
|
||||
state = VARIABLE_RAW;
|
||||
|
||||
} else
|
||||
state = WORD;
|
||||
break;
|
||||
|
||||
case VARIABLE:
|
||||
if (*e == '}') {
|
||||
const char *t;
|
||||
|
||||
t = strv_env_get_n(env, word+2, e-word-2, flags);
|
||||
|
||||
k = strappend(r, t);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
word = e+1;
|
||||
state = WORD;
|
||||
} else if (*e == ':') {
|
||||
if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
|
||||
/* Treat this as unsupported syntax, i.e. do no replacement */
|
||||
state = WORD;
|
||||
else {
|
||||
len = e-word-2;
|
||||
state = TEST;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TEST:
|
||||
if (*e == '-')
|
||||
state = DEFAULT_VALUE;
|
||||
else if (*e == '+')
|
||||
state = ALTERNATE_VALUE;
|
||||
else {
|
||||
state = WORD;
|
||||
break;
|
||||
}
|
||||
|
||||
test_value = e+1;
|
||||
break;
|
||||
|
||||
case DEFAULT_VALUE: /* fall through */
|
||||
case ALTERNATE_VALUE:
|
||||
assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
|
||||
|
||||
if (*e == '{') {
|
||||
nest++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*e != '}')
|
||||
break;
|
||||
|
||||
nest--;
|
||||
if (nest == 0) {
|
||||
const char *t;
|
||||
_cleanup_free_ char *v = NULL;
|
||||
|
||||
t = strv_env_get_n(env, word+2, len, flags);
|
||||
|
||||
if (t && state == ALTERNATE_VALUE)
|
||||
t = v = replace_env_n(test_value, e-test_value, env, flags);
|
||||
else if (!t && state == DEFAULT_VALUE)
|
||||
t = v = replace_env_n(test_value, e-test_value, env, flags);
|
||||
|
||||
k = strappend(r, t);
|
||||
if (!k)
|
||||
return NULL;
|
||||
|
||||
free_and_replace(r, k);
|
||||
|
||||
word = e+1;
|
||||
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_and_replace(r, k);
|
||||
|
||||
word = e--;
|
||||
i--;
|
||||
state = WORD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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) {
|
||||
char **ret, **i;
|
||||
size_t k = 0, l = 0;
|
||||
|
||||
l = strv_length(argv);
|
||||
|
||||
ret = new(char*, l+1);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
STRV_FOREACH(i, argv) {
|
||||
|
||||
/* If $FOO appears as single word, replace it by the split up variable */
|
||||
if ((*i)[0] == '$' && !IN_SET((*i)[1], '{', '$')) {
|
||||
char *e;
|
||||
char **w, **m = NULL;
|
||||
size_t q;
|
||||
|
||||
e = strv_env_get(env, *i+1);
|
||||
if (e) {
|
||||
int r;
|
||||
|
||||
r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
|
||||
if (r < 0) {
|
||||
ret[k] = NULL;
|
||||
strv_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
m = NULL;
|
||||
|
||||
q = strv_length(m);
|
||||
l = l + q - 1;
|
||||
|
||||
w = reallocarray(ret, l + 1, sizeof(char *));
|
||||
if (!w) {
|
||||
ret[k] = NULL;
|
||||
strv_free(ret);
|
||||
strv_free(m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = w;
|
||||
if (m) {
|
||||
memcpy(ret + k, m, q * sizeof(char*));
|
||||
free(m);
|
||||
}
|
||||
|
||||
k += q;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
|
||||
ret[k] = replace_env(*i, env, 0);
|
||||
if (!ret[k]) {
|
||||
strv_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
ret[k] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int getenv_bool(const char *p) {
|
||||
const char *e;
|
||||
|
||||
e = getenv(p);
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
return parse_boolean(e);
|
||||
}
|
||||
|
||||
int getenv_bool_secure(const char *p) {
|
||||
const char *e;
|
||||
|
||||
e = secure_getenv(p);
|
||||
if (!e)
|
||||
return -ENXIO;
|
||||
|
||||
return parse_boolean(e);
|
||||
}
|
Reference in New Issue
Block a user