diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/posix/environment_posix.c | 134 | ||||
-rw-r--r-- | src/win32/environment_win32.c | 22 |
2 files changed, 156 insertions, 0 deletions
diff --git a/src/posix/environment_posix.c b/src/posix/environment_posix.c new file mode 100644 index 0000000..58b8fd2 --- /dev/null +++ b/src/posix/environment_posix.c @@ -0,0 +1,134 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <zix/environment.h> + +#include <zix/allocator.h> +#include <zix/string_view.h> + +#include <stdbool.h> +#include <string.h> + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +extern char** environ; + +static bool +is_path_delim(const char c) +{ + return c == '/' || c == ':' || c == '\0'; +} + +static bool +is_var_name_char(const char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +static const char* +find_env(const ZixStringView name) +{ + if (environ) { + for (size_t i = 0U; environ[i]; ++i) { + // Find the first character in this entry that doesn't match the name + const char* const entry = environ[i]; + size_t j = 0U; + while (j < name.length && entry[j] == name.data[j]) { + ++j; + } + + if (j == name.length && entry[j] == '=') { + return entry + j + 1U; + } + } + } + + return NULL; +} + +// Append suffix to dst, update dst_len, and return the realloc'd result +static char* +append_str(ZixAllocator* const allocator, + size_t* const dst_len, + char* const dst, + const size_t suffix_len, + const char* const suffix) +{ + const size_t out_len = *dst_len + suffix_len; + char* const out = (char*)zix_realloc(allocator, dst, out_len + 1U); + if (!out) { + zix_free(allocator, dst); + return NULL; + } + + memcpy(out + *dst_len, suffix, suffix_len); + out[(*dst_len += suffix_len)] = '\0'; + return out; +} + +// Append the value of the environment variable var to dst if one is set +static char* +append_var(ZixAllocator* const allocator, + size_t* const dst_len, + char* const dst, + const size_t ref_len, + const char* const ref) +{ + // Get value from environment + const ZixStringView var = zix_substring(ref + 1U, ref_len - 1U); + const char* val = find_env(var); + if (val) { + return append_str(allocator, dst_len, dst, strlen(val), val); + } + + // No value found, append variable reference as-is + return append_str(allocator, dst_len, dst, ref_len, ref); +} + +char* +zix_expand_environment_strings(ZixAllocator* const allocator, + const char* const string) +{ + char* out = NULL; + size_t len = 0U; + + size_t start = 0U; // Start of current chunk to copy + for (size_t s = 0U; string[s];) { + const char c = string[s]; + if (c == '$' && is_var_name_char(string[s + 1U])) { + // Hit $ (variable reference like $VAR_NAME) + for (size_t t = 1U;; ++t) { + if (!is_var_name_char(string[s + t])) { + const char* const prefix = string + start; + const size_t prefix_len = s - start; + if ((prefix_len && + !(out = append_str(allocator, &len, out, prefix_len, prefix))) || + !(out = append_var(allocator, &len, out, t, string + s))) { + return NULL; + } + start = s = t; + break; + } + } + } else if (c == '~' && is_path_delim(string[s + 1U])) { + // Hit ~ before delimiter or end of string (home directory reference) + const char* const prefix = string + start; + const size_t prefix_len = s - start; + if ((prefix_len && + !(out = append_str(allocator, &len, out, prefix_len, prefix))) || + !(out = append_var(allocator, &len, out, 5U, "$HOME"))) { + return NULL; + } + start = ++s; + } else { + ++s; + } + } + + if (string[start]) { + const char* const tail = string + start; + const size_t tail_len = strlen(tail); + out = append_str(allocator, &len, out, tail_len, tail); + } + + return out; +} diff --git a/src/win32/environment_win32.c b/src/win32/environment_win32.c new file mode 100644 index 0000000..e7a0ee3 --- /dev/null +++ b/src/win32/environment_win32.c @@ -0,0 +1,22 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <zix/environment.h> + +#include <windows.h> + +char* +zix_expand_environment_strings(ZixAllocator* const allocator, + const char* const string) +{ + const DWORD size = ExpandEnvironmentStrings(string, NULL, 0U); + if (!size) { + return NULL; + } + + char* const out = (char*)zix_calloc(allocator, 1U, size + 1U); + if (out) { + ExpandEnvironmentStrings(string, out, size + 1U); + } + return out; +} |