From eae6daecf8a45aa34cd1913b4d95e956cc52ff45 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Sat, 9 Jul 2022 11:27:09 -0500 Subject: [PATCH] Feature: Add missing function f_environment_get_all(). A get all environment variables function should exist. The POSIX/libc standards do not seem to provide one. Utilize the "environ" variable to load all of the environment variables into a string map array. --- level_0/f_environment/c/environment.c | 39 ++++++++++++++++++++ level_0/f_environment/c/environment.h | 21 +++++++++++ level_0/f_environment/data/build/settings-tests | 2 +- .../tests/unit/c/test-environment-get_all.c | 42 ++++++++++++++++++++++ .../tests/unit/c/test-environment-get_all.h | 27 ++++++++++++++ .../f_environment/tests/unit/c/test-environment.c | 3 ++ .../f_environment/tests/unit/c/test-environment.h | 1 + 7 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 level_0/f_environment/tests/unit/c/test-environment-get_all.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-get_all.h diff --git a/level_0/f_environment/c/environment.c b/level_0/f_environment/c/environment.c index 9f77e75..64b1866 100644 --- a/level_0/f_environment/c/environment.c +++ b/level_0/f_environment/c/environment.c @@ -4,6 +4,9 @@ extern "C" { #endif +// for loading all current environment variables. +extern char **environ; + #ifndef _di_f_environment_clear_ f_status_t f_environment_clear(void) { @@ -69,6 +72,42 @@ extern "C" { } #endif // _di_f_environment_get_ +#ifndef _di_f_environment_get_all_ + f_status_t f_environment_get_all(f_string_maps_t * const environment) { + #ifndef _di_level_0_parameter_checking_ + if (!environment) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + f_status_t status = F_none; + char *at = 0; + f_string_map_t map = f_string_map_t_initialize; + + // Copy all environment variables over when a custom define is used. + for (char **string = environ; *string; string++) { + + map.name.string = 0; + map.value.string = 0; + + map.name.used = 0; + map.value.used = 0; + + at = index(*string, f_string_ascii_equal_s.string[0]); + if (!at || at == *string) continue; + + map.name.string = *string; + map.name.used = at - *string; + + map.value.string = at + 1; + map.value.used = strlen(at + 1); + + status = f_string_maps_append(map, environment); + if (F_status_is_error(status)) return status; + } // for + + return F_none; + } +#endif // _di_f_environment_get_all_ + #ifndef _di_f_environment_secure_is_ f_status_t f_environment_secure_is(void) { diff --git a/level_0/f_environment/c/environment.h b/level_0/f_environment/c/environment.h index 92f03be..e00fcf1 100644 --- a/level_0/f_environment/c/environment.h +++ b/level_0/f_environment/c/environment.h @@ -103,6 +103,27 @@ extern "C" { #endif // _di_f_environment_get_ /** + * Get all environment variables. + * + * The variables are copied into a new dynamically allocated map and is safe to alter. + * + * @param environment + * An array of maps containing all available environment variable name and value pairs. + * + * @return + * F_none on success. + * + * F_parameter (with error bit) if a parameter is invalid. + * + * Errors (with error bit) from: f_string_maps_append(). + * + * @see f_string_maps_append() + */ +#ifndef _di_f_environment_get_all_ + extern f_status_t f_environment_get_all(f_string_maps_t * const environment); +#endif // _di_f_environment_get_all_ + +/** * Check to see if the environment is secure for calling getenv() safely for "secure execution". * * This is intended to closely mimic the checks secure_getenv(). diff --git a/level_0/f_environment/data/build/settings-tests b/level_0/f_environment/data/build/settings-tests index 654d7de..212b258 100644 --- a/level_0/f_environment/data/build/settings-tests +++ b/level_0/f_environment/data/build/settings-tests @@ -25,7 +25,7 @@ build_language c build_libraries -lc -lcmocka build_libraries-individual -lf_memory -lf_string -lf_environment -build_sources_program test-environment-clear.c test-environment-exists.c test-environment-get.c test-environment-secure_is.c test-environment-set.c test-environment-unset.c +build_sources_program test-environment-clear.c test-environment-exists.c test-environment-get.c test-environment-get_all.c test-environment-secure_is.c test-environment-set.c test-environment-unset.c build_sources_program test-environment.c build_script no diff --git a/level_0/f_environment/tests/unit/c/test-environment-get_all.c b/level_0/f_environment/tests/unit/c/test-environment-get_all.c new file mode 100644 index 0000000..84fe615 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-get_all.c @@ -0,0 +1,42 @@ +#include "test-environment.h" +#include "test-environment-get.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_get_all__parameter_checking(void **state) { + + { + const f_status_t status = f_environment_get_all(0); + + assert_int_equal(status, F_status_set_error(F_parameter)); + } +} + +void test__f_environment_get_all__works(void **state) { + + const f_string_static_t name = macro_f_string_static_t_initialize("test", 0, 4); + const f_string_static_t value = macro_f_string_static_t_initialize("works", 0, 5); + + f_string_maps_t environment = f_string_maps_t_initialize; + + { + // Cannot easily mock because this is used: extern char **environ; + clearenv(); + setenv(name.string, value.string, true); + + const f_status_t status = f_environment_get_all(&environment); + + assert_int_equal(status, F_none); + assert_int_equal(environment.used, 1); + assert_string_equal(environment.array[0].name.string, name.string); + assert_string_equal(environment.array[0].value.string, value.string); + } + + f_string_maps_resize(0, &environment); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-get_all.h b/level_0/f_environment/tests/unit/c/test-environment-get_all.h new file mode 100644 index 0000000..4d77ef2 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-get_all.h @@ -0,0 +1,27 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.6 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_get_all_h +#define _TEST__F_environment_get_all_h + +/** + * Test that parameter checking works as expected. + * + * @see f_environment_get_all() + */ +extern void test__f_environment_get_all__parameter_checking(void **state); + +/** + * Test that function works. + * + * @see f_environment_get_all() + */ +extern void test__f_environment_get_all__works(void **state); + +#endif // _TEST__F_environment_get_all_h diff --git a/level_0/f_environment/tests/unit/c/test-environment.c b/level_0/f_environment/tests/unit/c/test-environment.c index 8096ba7..21986a4 100644 --- a/level_0/f_environment/tests/unit/c/test-environment.c +++ b/level_0/f_environment/tests/unit/c/test-environment.c @@ -30,6 +30,8 @@ int main(void) { cmocka_unit_test(test__f_environment_get__returns_data_not), cmocka_unit_test(test__f_environment_get__works), + cmocka_unit_test(test__f_environment_get_all__works), + cmocka_unit_test(test__f_environment_secure_is__fails), cmocka_unit_test(test__f_environment_secure_is__works), @@ -45,6 +47,7 @@ int main(void) { // f_environment_clear() doesn't use parameter checking. // f_environment_exists() doesn't use parameter checking. cmocka_unit_test(test__f_environment_get__parameter_checking), + cmocka_unit_test(test__f_environment_get_all__parameter_checking), // f_environment_secure_is() doesn't use parameter checking. // f_environment_set() doesn't use parameter checking. // f_environment_unset() doesn't use parameter checking. diff --git a/level_0/f_environment/tests/unit/c/test-environment.h b/level_0/f_environment/tests/unit/c/test-environment.h index 62b2eab..b66d791 100644 --- a/level_0/f_environment/tests/unit/c/test-environment.h +++ b/level_0/f_environment/tests/unit/c/test-environment.h @@ -29,6 +29,7 @@ #include "test-environment-clear.h" #include "test-environment-exists.h" #include "test-environment-get.h" +#include "test-environment-get_all.h" #include "test-environment-secure_is.h" #include "test-environment-set.h" #include "test-environment-unset.h" -- 1.8.3.1