mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:57:35 +00:00
LibCore: Introduce Core::Environment wrapper
Core::System already had some wrappers for *env() functions, which I've copied over. But 1) the set of functions there was incomplete, and 2) the environment feels like an object in its own right, so let's treat it as one. :^) Also add `Core::Environment::has(StringView)` for situations where we just care if a variable is defined.
This commit is contained in:
parent
55dc69625a
commit
b9dc2d7ebf
3 changed files with 251 additions and 0 deletions
169
Userland/Libraries/LibCore/Environment.cpp
Normal file
169
Userland/Libraries/LibCore/Environment.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021-2022, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||
* Copyright (c) 2021-2024, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2022, Matthias Zimmerman <matthias291999@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "Environment.h"
|
||||
#include <AK/ByteString.h>
|
||||
|
||||
#if defined(AK_OS_MACOS)
|
||||
# include <crt_externs.h>
|
||||
#else
|
||||
extern char** environ;
|
||||
#endif
|
||||
|
||||
namespace Core::Environment {
|
||||
|
||||
char** raw_environ()
|
||||
{
|
||||
#if defined(AK_OS_MACOS)
|
||||
return *_NSGetEnviron();
|
||||
#else
|
||||
return environ;
|
||||
#endif
|
||||
}
|
||||
|
||||
Entry Entry::from_chars(char const* input)
|
||||
{
|
||||
return Entry::from_string({ input, strlen(input) });
|
||||
}
|
||||
|
||||
Entry Entry::from_string(StringView input)
|
||||
{
|
||||
auto split_index = input.find('=');
|
||||
if (!split_index.has_value()) {
|
||||
return Entry {
|
||||
.full_entry = input,
|
||||
.name = input,
|
||||
.value = ""sv,
|
||||
};
|
||||
}
|
||||
|
||||
return Entry {
|
||||
.full_entry = input,
|
||||
.name = input.substring_view(0, *split_index),
|
||||
.value = input.substring_view(*split_index + 1),
|
||||
};
|
||||
}
|
||||
|
||||
EntryIterator EntryIterator::begin()
|
||||
{
|
||||
return EntryIterator(0);
|
||||
}
|
||||
|
||||
EntryIterator EntryIterator::end()
|
||||
{
|
||||
auto environment = raw_environ();
|
||||
|
||||
size_t env_count = 0;
|
||||
for (size_t i = 0; environment[i]; ++i)
|
||||
++env_count;
|
||||
return EntryIterator(env_count);
|
||||
}
|
||||
|
||||
EntryIterator entries()
|
||||
{
|
||||
return EntryIterator::begin();
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
auto environment = raw_environ();
|
||||
|
||||
size_t environ_size = 0;
|
||||
while (environment[environ_size])
|
||||
++environ_size;
|
||||
|
||||
return environ_size;
|
||||
}
|
||||
|
||||
bool has(StringView name)
|
||||
{
|
||||
return get(name).has_value();
|
||||
}
|
||||
|
||||
Optional<StringView> get(StringView name, [[maybe_unused]] SecureOnly secure)
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append(name);
|
||||
builder.append('\0');
|
||||
// Note the explicit null terminators above.
|
||||
|
||||
#if defined(AK_OS_MACOS)
|
||||
char* result = ::getenv(builder.string_view().characters_without_null_termination());
|
||||
#else
|
||||
char* result;
|
||||
if (secure == SecureOnly::Yes) {
|
||||
result = ::secure_getenv(builder.string_view().characters_without_null_termination());
|
||||
} else {
|
||||
result = ::getenv(builder.string_view().characters_without_null_termination());
|
||||
}
|
||||
#endif
|
||||
if (result)
|
||||
return StringView { result, strlen(result) };
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> set(StringView name, StringView value, Overwrite overwrite)
|
||||
{
|
||||
auto builder = TRY(StringBuilder::create());
|
||||
TRY(builder.try_append(name));
|
||||
TRY(builder.try_append('\0'));
|
||||
TRY(builder.try_append(value));
|
||||
TRY(builder.try_append('\0'));
|
||||
// Note the explicit null terminators above.
|
||||
auto c_name = builder.string_view().characters_without_null_termination();
|
||||
auto c_value = c_name + name.length() + 1;
|
||||
auto rc = ::setenv(c_name, c_value, overwrite == Overwrite::Yes ? 1 : 0);
|
||||
if (rc < 0)
|
||||
return Error::from_errno(errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> unset(StringView name)
|
||||
{
|
||||
auto builder = TRY(StringBuilder::create());
|
||||
TRY(builder.try_append(name));
|
||||
TRY(builder.try_append('\0'));
|
||||
|
||||
// Note the explicit null terminator above.
|
||||
auto rc = ::unsetenv(builder.string_view().characters_without_null_termination());
|
||||
if (rc < 0)
|
||||
return Error::from_errno(errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> put(StringView env)
|
||||
{
|
||||
#if defined(AK_OS_SERENITY)
|
||||
auto rc = ::serenity_putenv(env.characters_without_null_termination(), env.length());
|
||||
#else
|
||||
// Leak somewhat unavoidable here due to the putenv API.
|
||||
auto leaked_new_env = strndup(env.characters_without_null_termination(), env.length());
|
||||
auto rc = ::putenv(leaked_new_env);
|
||||
#endif
|
||||
if (rc < 0)
|
||||
return Error::from_errno(errno);
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> clear()
|
||||
{
|
||||
#if defined(AK_OS_MACOS)
|
||||
auto environment = raw_environ();
|
||||
for (size_t environ_size = 0; environment[environ_size]; ++environ_size) {
|
||||
environment[environ_size] = NULL;
|
||||
}
|
||||
#else
|
||||
auto rc = ::clearenv();
|
||||
if (rc < 0)
|
||||
return Error::from_errno(errno);
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue