OS: Add unset_environment, better validate input

Instead of returning an undocumented boolean error code, we do the
validation checks that should ensure a successful result.

Based on:
- https://linux.die.net/man/3/setenv
- https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable
This commit is contained in:
Rémi Verschelde 2023-01-16 14:26:14 +01:00
parent 04a39ecd84
commit 818a9e99a4
No known key found for this signature in database
GPG key ID: C3336907360768E1
8 changed files with 51 additions and 17 deletions

View file

@ -322,8 +322,12 @@ String OS::get_environment(const String &p_var) const {
return ::OS::get_singleton()->get_environment(p_var);
}
bool OS::set_environment(const String &p_var, const String &p_value) const {
return ::OS::get_singleton()->set_environment(p_var, p_value);
void OS::set_environment(const String &p_var, const String &p_value) const {
::OS::get_singleton()->set_environment(p_var, p_value);
}
void OS::unset_environment(const String &p_var) const {
::OS::get_singleton()->unset_environment(p_var);
}
String OS::get_name() const {
@ -548,9 +552,10 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
ClassDB::bind_method(D_METHOD("get_environment", "variable"), &OS::get_environment);
ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &OS::set_environment);
ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
ClassDB::bind_method(D_METHOD("unset_environment", "variable"), &OS::unset_environment);
ClassDB::bind_method(D_METHOD("get_name"), &OS::get_name);
ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_name);

View file

@ -162,7 +162,8 @@ public:
bool has_environment(const String &p_var) const;
String get_environment(const String &p_var) const;
bool set_environment(const String &p_var, const String &p_value) const;
void set_environment(const String &p_var, const String &p_value) const;
void unset_environment(const String &p_var) const;
String get_name() const;
String get_distribution_name() const;

View file

@ -167,7 +167,8 @@ public:
virtual bool has_environment(const String &p_var) const = 0;
virtual String get_environment(const String &p_var) const = 0;
virtual bool set_environment(const String &p_var, const String &p_value) const = 0;
virtual void set_environment(const String &p_var, const String &p_value) const = 0;
virtual void unset_environment(const String &p_var) const = 0;
virtual String get_name() const = 0;
virtual String get_distribution_name() const = 0;

View file

@ -592,12 +592,12 @@
</description>
</method>
<method name="set_environment" qualifiers="const">
<return type="bool" />
<return type="void" />
<param index="0" name="variable" type="String" />
<param index="1" name="value" type="String" />
<description>
Sets the value of the environment variable [param variable] to [param value]. The environment variable will be set for the Godot process and any process executed with [method execute] after running [method set_environment]. The environment variable will [i]not[/i] persist to processes run after the Godot process was terminated.
[b]Note:[/b] Double-check the casing of [param variable]. Environment variable names are case-sensitive on all platforms except Windows.
[b]Note:[/b] Environment variable names are case-sensitive on all platforms except Windows. The [param variable] name cannot be empty or include the [code]=[/code] character. On Windows, there is a 32767 characters limit for the combined length of [param variable], [param value], and the [code]=[/code] and null terminator characters that will be registered in the environment block.
</description>
</method>
<method name="set_restart_on_exit">
@ -637,6 +637,14 @@
[b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows.
</description>
</method>
<method name="unset_environment" qualifiers="const">
<return type="void" />
<param index="0" name="variable" type="String" />
<description>
Removes the environment [param variable] from the current environment, if it exists. The environment variable will be removed for the Godot process and any process executed with [method execute] after running [method unset_environment]. The removal of the environment variable will [i]not[/i] persist to processes run after the Godot process was terminated.
[b]Note:[/b] Environment variable names are case-sensitive on all platforms except Windows. The [param variable] name cannot be empty or include the [code]=[/code] character.
</description>
</method>
</methods>
<members>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode" default="false">

View file

@ -411,10 +411,6 @@ bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
return true;
}
bool OS_Unix::has_environment(const String &p_var) const {
return getenv(p_var.utf8().get_data()) != nullptr;
}
String OS_Unix::get_locale() const {
if (!has_environment("LANG")) {
return "en";
@ -487,6 +483,10 @@ Error OS_Unix::set_cwd(const String &p_cwd) {
return OK;
}
bool OS_Unix::has_environment(const String &p_var) const {
return getenv(p_var.utf8().get_data()) != nullptr;
}
String OS_Unix::get_environment(const String &p_var) const {
if (getenv(p_var.utf8().get_data())) {
return getenv(p_var.utf8().get_data());
@ -494,8 +494,15 @@ String OS_Unix::get_environment(const String &p_var) const {
return "";
}
bool OS_Unix::set_environment(const String &p_var, const String &p_value) const {
return setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ true) == 0;
void OS_Unix::set_environment(const String &p_var, const String &p_value) const {
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
int err = setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ 1);
ERR_FAIL_COND_MSG(err != 0, vformat("Failed setting environment variable '%s', the system is out of memory.", p_var));
}
void OS_Unix::unset_environment(const String &p_var) const {
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
unsetenv(p_var.utf8().get_data());
}
String OS_Unix::get_user_data_dir() const {

View file

@ -81,7 +81,9 @@ public:
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
virtual bool set_environment(const String &p_var, const String &p_value) const override;
virtual void set_environment(const String &p_var, const String &p_value) const override;
virtual void unset_environment(const String &p_var) const override;
virtual String get_locale() const override;
virtual void initialize_debugging() override;

View file

@ -1166,8 +1166,17 @@ String OS_Windows::get_environment(const String &p_var) const {
return "";
}
bool OS_Windows::set_environment(const String &p_var, const String &p_value) const {
return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data()));
void OS_Windows::set_environment(const String &p_var, const String &p_value) const {
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
Char16String var = p_var.utf16();
Char16String value = p_value.utf16();
ERR_FAIL_COND_MSG(var.length() + value.length() + 2 > 32767, vformat("Invalid definition for environment variable '%s', cannot exceed 32767 characters.", p_var));
SetEnvironmentVariableW((LPCWSTR)(var.get_data()), (LPCWSTR)(value.get_data()));
}
void OS_Windows::unset_environment(const String &p_var) const {
ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains("="), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), nullptr); // Null to delete.
}
String OS_Windows::get_stdin_string() {

View file

@ -186,7 +186,8 @@ public:
virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override;
virtual bool set_environment(const String &p_var, const String &p_value) const override;
virtual void set_environment(const String &p_var, const String &p_value) const override;
virtual void unset_environment(const String &p_var) const override;
virtual Vector<String> get_system_fonts() const override;
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;