From 660d785237d53d680b8ca5717714a0892c2ea904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gu=C3=A9len?= Date: Sun, 16 Feb 2025 13:07:33 +0100 Subject: [PATCH] ENV: A small set of functions for internal env --- Makefile | 2 + src/env_get.c | 17 +++- src/env_manip.c | 178 +++++++++++++++++++++++++++++++++++++++++ src/env_manip.h | 6 +- src/env_manip_utils.c | 97 ++++++++++++++++++++++ tests/Makefile | 4 +- tests/env_manip.c | 36 --------- tests/test_env_manip.c | 148 ++++++++++++++++++++++++++++++++++ 8 files changed, 447 insertions(+), 41 deletions(-) create mode 100644 src/env_manip.c create mode 100644 src/env_manip_utils.c delete mode 100644 tests/env_manip.c create mode 100644 tests/test_env_manip.c diff --git a/Makefile b/Makefile index cc276a8..635e9c8 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,8 @@ endif export CFLAGS srcs = \ src/env_get.c \ + src/env_manip.c \ + src/env_manip_utils.c \ src/parser/matchers/metacharacter.c \ objs = $(srcs:.c=.o) diff --git a/src/env_get.c b/src/env_get.c index f10c8fe..449c458 100644 --- a/src/env_get.c +++ b/src/env_get.c @@ -6,7 +6,7 @@ /* By: jguelen +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/02/14 18:43:38 by jguelen #+# #+# */ -/* Updated: 2025/02/18 16:23:24 by khais ### ########.fr */ +/* Updated: 2025/02/18 16:33:10 by khais ### ########.fr */ /* */ /* ************************************************************************** */ @@ -66,3 +66,18 @@ char *envp_get_val(char *line) val_pointer++; return (ft_strdup(val_pointer)); } + +/* +** Get the value corresponding to a given key in the given environment structure +** +** If not found, return null +*/ +char *env_get_val(t_env *env, char *key) +{ + t_env *node; + + node = env_find_node_bykey(env, key); + if (node) + return (node->value); + return (NULL); +} diff --git a/src/env_manip.c b/src/env_manip.c new file mode 100644 index 0000000..1b58080 --- /dev/null +++ b/src/env_manip.c @@ -0,0 +1,178 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* env_manip.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/12 18:29:12 by jguelen #+# #+# */ +/* Updated: 2025/02/18 18:41:30 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "env_manip.h" +#include "libft.h" + +/* +** Remove the first env node of the given linked list where the key matches the +** given key. +** +** In any given env linked list, there should only be one entry for a given key. +** +** If the linked list is empty or the pointer is NULL, nothing is done. +** +** If the linked list does not contain an element matching the given key, +** nothing is done. +*/ +void env_rm_entry(t_env **env, char *key) +{ + t_env *current; + t_env *next; + + if (env == NULL || (*env) == NULL) + return ; + if (strncmp((*env)->key, key, INT_MAX) == 0) + { + next = (*env)->next; + env_destroy_node(*env); + (*env) = next; + return ; + } + current = *env; + while (current->next) + { + if (strncmp(current->next->key, key, INT_MAX) == 0) + { + next = current->next; + current->next = next->next; + env_destroy_node(next); + return ; + } + current = current->next; + } +} + +/* +** Add the given element to the end of the given linked list. +*/ +static void env_add_back(t_env **env, t_env *new) +{ + t_env *last; + + last = *env; + if (last == NULL) + { + *env = new; + return ; + } + while (last->next) + last = last->next; + last->next = new; +} + +/* +** In the given env linked list, if an element with the given key exist, set its +** value to the one provided. If no such element exist, create a new one with +** the provided value. +** +** The provided key and value must be allocated. In case of error, they will be +** freed. In case a node matching the given key is found the provided key value +** is freed. +** +** If key or value are null, both are freed and NULL is returned. +** +** If key is an empty string, key and value is freed and a non-error value is +** returned. +** +** If there is a failure allocating a new node, NULL is returned. +** +** Returns a pointer to the first element in the linked list, or NULL on error. +*/ +t_env *env_set_entry(t_env **env, char *key, char *value) +{ + t_env *node; + + if (key == NULL || value == NULL) + return (free(key), free(value), NULL); + if (*key == '\0') + return (free(key), free(value), *env); + node = env_find_node_bykey(*env, key); + if (node == NULL) + { + node = ft_calloc(1, sizeof(t_env)); + if (!node) + return (free(key), free(value), NULL); + node->key = key; + node->value = value; + env_add_back(env, node); + } + else + { + free(node->value); + free(key); + node->value = value; + } + return (*env); +} + +/* +** read an envp structure, and create a t_env linked list containing the same +** information. +** +** the envp structure is not freed. +** +** in case of error, all memory is freed and null is returned. +** +** no checks additional checks than those of envp_get_key and envp_get_val are +** performed +*/ +t_env *env_from_envp(char **envp) +{ + t_env *env; + + env = NULL; + while (*envp) + { + if (env_set_entry(&env, envp_get_key(*envp), + envp_get_val(*envp)) == NULL) + { + env_destroy(env); + return (NULL); + } + envp++; + } + return (env); +} + +/* +** Create an envp structure from the given env linked list. +** +** The linked list is not destroyed. +** +** in case of allocation error, all memory is freed and NULL is returned. +*/ +char **envp_from_env(t_env *env) +{ + char **new_envp; + size_t size; + size_t i; + + size = env_get_size(env); + new_envp = ft_calloc(size + 1, sizeof(char *)); + if (new_envp == NULL) + return (NULL); + i = 0; + while (i < size) + { + new_envp[i] = ft_strjoin_sepc(env->key, env->value, '='); + if (new_envp[i] == NULL) + { + envp_destroy(new_envp); + return (NULL); + } + i++; + env = env->next; + } + new_envp[size] = NULL; + return (new_envp); +} diff --git a/src/env_manip.h b/src/env_manip.h index 8328af2..30888f3 100644 --- a/src/env_manip.h +++ b/src/env_manip.h @@ -6,7 +6,7 @@ /* By: jguelen +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/02/14 13:46:39 by jguelen #+# #+# */ -/* Updated: 2025/02/16 12:55:27 by jguelen ### ########.fr */ +/* Updated: 2025/02/18 16:47:34 by khais ### ########.fr */ /* */ /* ************************************************************************** */ @@ -36,8 +36,8 @@ t_env *env_from_envp(char **envp); /*env_manip_utils*/ size_t env_get_size(t_env *env); void env_destroy_node(t_env *node); -void env_destroy(t_env **env); -void envp_destroy_envp(char **envp); +void env_destroy(t_env *env); +void envp_destroy(char **envp); t_env *env_find_node_bykey(t_env *env, char *key); #endif diff --git a/src/env_manip_utils.c b/src/env_manip_utils.c new file mode 100644 index 0000000..8a853d3 --- /dev/null +++ b/src/env_manip_utils.c @@ -0,0 +1,97 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* env_manip_utils.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/14 18:31:38 by jguelen #+# #+# */ +/* Updated: 2025/02/18 16:40:32 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "env_manip.h" +#include "libft.h" + +/* +** Free all memory related to a given envp structure +*/ +void envp_destroy(char **envp) +{ + int i; + + if (!envp) + return ; + i = 0; + while (envp[i]) + { + free(envp[i]); + i++; + } + free(envp); +} + +/* +** Free the given env node. +*/ +void env_destroy_node(t_env *env) +{ + if (!env) + return ; + free(env->key); + free(env->value); + free(env); +} + +/* +** Free the given env linked list. +*/ +void env_destroy(t_env *env) +{ + t_env *next; + + if (!env) + return ; + while (env) + { + next = env->next; + env_destroy_node(env); + env = next; + } +} + +/* +** Get the number of mappings in the givne env linked list +*/ +size_t env_get_size(t_env *env) +{ + size_t nb_elem; + + nb_elem = 0; + while (env) + { + nb_elem++; + env = env->next; + } + return (nb_elem); +} + +/* +** Find and return a pointer to the node in the given env linked list where the +** key matches the given key. +** +** If the node is not found, return NULL. +** +** Note that this is a pointer to the middle of the linked list, the node is not +** copied. +*/ +t_env *env_find_node_bykey(t_env *env, char *key) +{ + while (env) + { + if (ft_strncmp(env->key, key, INT_MAX) == 0) + return (env); + env = env->next; + } + return (NULL); +} diff --git a/tests/Makefile b/tests/Makefile index f8a7a92..7d76d89 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,7 @@ +# make gets confused if a file with the same name exists in the sources, so some +# file are prefixed with test_ rawtests = \ - env_manip \ + test_env_manip \ metacharacters \ tests = $(addprefix test_,$(rawtests)) diff --git a/tests/env_manip.c b/tests/env_manip.c deleted file mode 100644 index 0d01451..0000000 --- a/tests/env_manip.c +++ /dev/null @@ -1,36 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* env_manip.c :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: khais +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2025/02/18 15:11:14 by khais #+# #+# */ -/* Updated: 2025/02/18 16:12:23 by khais ### ########.fr */ -/* */ -/* ************************************************************************** */ - -#include "../src/env_manip.h" -#include "libft.h" -#include - -static void test_envp_parsing(char *line, char *expected_key, char *expected_value) -{ - char *got_key = envp_get_key(line); - char *got_value = envp_get_val(line); - ft_dprintf(STDERR_FILENO, "for envp value '%s', expecting key to eq '%s', and got '%s'\n", line, expected_key, got_key); - assert(expected_key == got_key || ft_strcmp(expected_key, got_key) == 0); - ft_dprintf(STDERR_FILENO, "for envp value '%s', expecting value to eq '%s', and got '%s'\n", line, expected_value, got_value); - assert(expected_value == got_value || ft_strcmp(expected_value, got_value) == 0); - free(got_key); - free(got_value); -} - -int main(void) { - test_envp_parsing("SHELL=/bin/fish", "SHELL", "/bin/fish"); - test_envp_parsing("=/bin/fish", "", "/bin/fish"); - test_envp_parsing(NULL, NULL, NULL); - test_envp_parsing("", NULL, NULL); - test_envp_parsing("VARNAME", "VARNAME", ""); - return (0); -} diff --git a/tests/test_env_manip.c b/tests/test_env_manip.c new file mode 100644 index 0000000..274357d --- /dev/null +++ b/tests/test_env_manip.c @@ -0,0 +1,148 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* test_env_manip.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: khais +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/18 15:11:14 by khais #+# #+# */ +/* Updated: 2025/02/19 12:23:50 by khais ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "../src/env_manip.h" +#include "libft.h" +#include "testutil.h" +#include "unistd.h" +#include + +static void test_envp_parsing(char *line, char *expected_key, char *expected_value) +{ + char *got_key = envp_get_key(line); + char *got_value = envp_get_val(line); + ft_dprintf(STDERR_FILENO, "for envp value '%s', expecting key to eq '%s', and got '%s'\n", line, expected_key, got_key); + assert(expected_key == got_key || ft_strcmp(expected_key, got_key) == 0); + ft_dprintf(STDERR_FILENO, "for envp value '%s', expecting value to eq '%s', and got '%s'\n", line, expected_value, got_value); + assert(expected_value == got_value || ft_strcmp(expected_value, got_value) == 0); + free(got_key); + free(got_value); +} + +static void assert_env_value(t_env *env, char *key, char *value) +{ + char *got_value = env_get_val(env, key); + ft_dprintf(STDERR_FILENO, "expecting %s=%s to exist, and got %s=%s\n", key, value, key, got_value); + assert(ft_strcmp(value, got_value) == 0); +} + +static void test_env_set_entry_empty_env(void) +{ + t_env *env; + + env = NULL; + assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL); + assert_env_value(env, "VAR", "hello"); + assert(1 == env_get_size(env)); + env_destroy(env); +} + +static void test_env_set_entry_existing_value(void) +{ + t_env *env; + + env = NULL; + assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL); + assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("there")) != NULL); + assert_env_value(env, "VAR", "there"); + assert(1 == env_get_size(env)); + env_destroy(env); +} + +static void test_env_set_entry_multiple(void) +{ + t_env *env; + + env = NULL; + assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL); + assert(env_set_entry(&env, ft_strdup("SHELL"), ft_strdup("/bin/bash")) != NULL); + assert(env_set_entry(&env, ft_strdup("TERM"), ft_strdup("xterm-kitty")) != NULL); + assert_env_value(env, "VAR", "hello"); + assert_env_value(env, "SHELL", "/bin/bash"); + assert_env_value(env, "TERM", "xterm-kitty"); + assert(3 == env_get_size(env)); + env_destroy(env); +} + +static void test_env_set_entry_nullargs(void) +{ + t_env *env; + + env = NULL; + assert(env_set_entry(&env, ft_strdup("VAR"), ft_strdup("hello")) != NULL); + assert(env_set_entry(&env, NULL, ft_strdup("hello")) == NULL); + assert(env_set_entry(&env, ft_strdup("VAR"), NULL) == NULL); + assert(env_set_entry(&env, ft_strdup(""), ft_strdup("value")) == env); + assert_env_value(env, "VAR", "hello"); + assert(1 == env_get_size(env)); + env_destroy(env); +} + +static void test_env_from_envp(void) +{ + + t_env *env; + char *envp[] = {"VAR=hello", "SHELL=/bin/bash", "TERM=xterm-kitty", NULL}; + + env = env_from_envp(envp); + assert(env != NULL); + assert_env_value(env, "VAR", "hello"); + assert_env_value(env, "SHELL", "/bin/bash"); + assert_env_value(env, "TERM", "xterm-kitty"); + assert(3 == env_get_size(env)); + env_destroy(env); +} + +static void test_env_from_envp_invalid(void) +{ + + t_env *env; + char *envp[] = {"VAR=hello", "", "TERM=xterm-kitty", NULL}; + + ft_dprintf(STDERR_FILENO, "test_env_from_envp\n"); + env = env_from_envp(envp); + assert(env == NULL); +} + +static void test_envp_from_env(void) +{ + t_env *env; + char *envp[] = {"VAR=hello", "SHELL=/bin/bash", "TERM=xterm-kitty", NULL}; + + env = env_from_envp(envp); + char **got_envp = envp_from_env(env); + t_env *got_env = env_from_envp(got_envp); + assert(got_env != NULL); + assert_env_value(got_env, "VAR", "hello"); + assert_env_value(got_env, "SHELL", "/bin/bash"); + assert_env_value(got_env, "TERM", "xterm-kitty"); + assert(3 == env_get_size(got_env)); + env_destroy(env); + env_destroy(got_env); + envp_destroy(got_envp); +} + +int main(void) { + test_envp_parsing("SHELL=/bin/fish", "SHELL", "/bin/fish"); + test_envp_parsing("=/bin/fish", "", "/bin/fish"); + test_envp_parsing(NULL, NULL, NULL); + test_envp_parsing("", NULL, NULL); + test_envp_parsing("VARNAME", "VARNAME", ""); + test_env_set_entry_empty_env(); + test_env_set_entry_existing_value(); + test_env_set_entry_multiple(); + test_env_set_entry_nullargs(); + test_env_from_envp(); + test_env_from_envp_invalid(); + test_envp_from_env(); + return (0); +}