ENV: A small set of functions for internal env

This commit is contained in:
Jérôme Guélen 2025-02-16 13:07:33 +01:00 committed by Khaïs COLIN
parent 18a2835a7c
commit 660d785237
8 changed files with 447 additions and 41 deletions

View file

@ -20,6 +20,8 @@ endif
export CFLAGS export CFLAGS
srcs = \ srcs = \
src/env_get.c \ src/env_get.c \
src/env_manip.c \
src/env_manip_utils.c \
src/parser/matchers/metacharacter.c \ src/parser/matchers/metacharacter.c \
objs = $(srcs:.c=.o) objs = $(srcs:.c=.o)

View file

@ -6,7 +6,7 @@
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */ /* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/14 18:43:38 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++; val_pointer++;
return (ft_strdup(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);
}

178
src/env_manip.c Normal file
View file

@ -0,0 +1,178 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* env_manip.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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);
}

View file

@ -6,7 +6,7 @@
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */ /* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */ /* +#+#+#+#+#+ +#+ */
/* Created: 2025/02/14 13:46:39 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*/ /*env_manip_utils*/
size_t env_get_size(t_env *env); size_t env_get_size(t_env *env);
void env_destroy_node(t_env *node); void env_destroy_node(t_env *node);
void env_destroy(t_env **env); void env_destroy(t_env *env);
void envp_destroy_envp(char **envp); void envp_destroy(char **envp);
t_env *env_find_node_bykey(t_env *env, char *key); t_env *env_find_node_bykey(t_env *env, char *key);
#endif #endif

97
src/env_manip_utils.c Normal file
View file

@ -0,0 +1,97 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* env_manip_utils.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: jguelen <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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);
}

View file

@ -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 = \ rawtests = \
env_manip \ test_env_manip \
metacharacters \ metacharacters \
tests = $(addprefix test_,$(rawtests)) tests = $(addprefix test_,$(rawtests))

View file

@ -1,36 +0,0 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* env_manip.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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 <assert.h>
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);
}

148
tests/test_env_manip.c Normal file
View file

@ -0,0 +1,148 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* test_env_manip.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* 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 <assert.h>
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);
}