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); return ::OS::get_singleton()->get_environment(p_var);
} }
bool OS::set_environment(const String &p_var, const String &p_value) const { void OS::set_environment(const String &p_var, const String &p_value) const {
return ::OS::get_singleton()->set_environment(p_var, p_value); ::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 { 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("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("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("get_environment", "variable"), &OS::get_environment);
ClassDB::bind_method(D_METHOD("set_environment", "variable", "value"), &OS::set_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_name"), &OS::get_name);
ClassDB::bind_method(D_METHOD("get_distribution_name"), &OS::get_distribution_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; bool has_environment(const String &p_var) const;
String get_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_name() const;
String get_distribution_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 bool has_environment(const String &p_var) const = 0;
virtual String get_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_name() const = 0;
virtual String get_distribution_name() const = 0; virtual String get_distribution_name() const = 0;

View File

@ -592,12 +592,12 @@
</description> </description>
</method> </method>
<method name="set_environment" qualifiers="const"> <method name="set_environment" qualifiers="const">
<return type="bool" /> <return type="void" />
<param index="0" name="variable" type="String" /> <param index="0" name="variable" type="String" />
<param index="1" name="value" type="String" /> <param index="1" name="value" type="String" />
<description> <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. 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> </description>
</method> </method>
<method name="set_restart_on_exit"> <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. [b]Note:[/b] This method is implemented on Android, iOS, Web, Linux, macOS and Windows.
</description> </description>
</method> </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> </methods>
<members> <members>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode" default="false"> <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; 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 { String OS_Unix::get_locale() const {
if (!has_environment("LANG")) { if (!has_environment("LANG")) {
return "en"; return "en";
@ -487,6 +483,10 @@ Error OS_Unix::set_cwd(const String &p_cwd) {
return OK; 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 { String OS_Unix::get_environment(const String &p_var) const {
if (getenv(p_var.utf8().get_data())) { if (getenv(p_var.utf8().get_data())) {
return 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 ""; return "";
} }
bool OS_Unix::set_environment(const String &p_var, const String &p_value) const { void 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; 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 { 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 bool has_environment(const String &p_var) const override;
virtual String get_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 String get_locale() const override;
virtual void initialize_debugging() override; virtual void initialize_debugging() override;

View File

@ -1166,8 +1166,17 @@ String OS_Windows::get_environment(const String &p_var) const {
return ""; return "";
} }
bool OS_Windows::set_environment(const String &p_var, const String &p_value) const { void 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())); 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() { String OS_Windows::get_stdin_string() {

View File

@ -186,7 +186,8 @@ public:
virtual bool has_environment(const String &p_var) const override; virtual bool has_environment(const String &p_var) const override;
virtual String get_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 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; 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;