From e348040ea4a70f7b5541d2754787298ca8da7a04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Gu=C3=A9len?= Date: Fri, 28 Feb 2025 18:48:30 +0100 Subject: [PATCH] Subst: A norm NON-compliant and incomplete version The code does not update flags yet and is to be refactored to conform to the norm. Tests required but needing a new version of the wordsplitting code. --- Makefile | 1 + src/minishell.h | 21 ++++++ src/sig/sig.h | 5 +- src/subst/replace_substr.c | 87 +---------------------- src/subst/replace_substr.h | 4 +- src/subst/subst.h | 24 +++++++ src/subst/variable_subst.c | 138 +++++++++++++++++++++++++++++++++++++ src/subst/wildcard_exp.c | 29 ++++++++ tests/Makefile | 1 + tests/expansion.c | 109 +++++++++++++++++++++++++++++ 10 files changed, 328 insertions(+), 91 deletions(-) create mode 100644 src/minishell.h create mode 100644 src/subst/subst.h create mode 100644 src/subst/variable_subst.c create mode 100644 src/subst/wildcard_exp.c create mode 100644 tests/expansion.c diff --git a/Makefile b/Makefile index 8178cc3..23ba6dd 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,7 @@ srcs = \ src/postprocess/redirections/redirection_list.c \ src/postprocess/redirections/redirection_parsing.c \ src/postprocess/redirections/redirection_type.c \ + src/subst/replace_substr.c \ objs = $(srcs:.c=.o) export objs diff --git a/src/minishell.h b/src/minishell.h new file mode 100644 index 0000000..01e2f51 --- /dev/null +++ b/src/minishell.h @@ -0,0 +1,21 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* minishell.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/24 12:51:21 by jguelen #+# #+# */ +/* Updated: 2025/02/24 13:04:32 by jguelen ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef MINISHELL_H +# define MINISHELL_H + +typedef struct s_minishell +{ + int last_return_value; +} t_minishell; + +#endif diff --git a/src/sig/sig.h b/src/sig/sig.h index da48301..43a48df 100644 --- a/src/sig/sig.h +++ b/src/sig/sig.h @@ -6,17 +6,18 @@ /* By: jguelen +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/02/19 18:21:55 by jguelen #+# #+# */ -/* Updated: 2025/02/21 15:26:59 by jguelen ### ########.fr */ +/* Updated: 2025/02/26 12:26:56 by jguelen ### ########.fr */ /* */ /* ************************************************************************** */ #ifndef SIG_H # define SIG_H +# define _POSIX_C_SOURCE 200809L + # include "libft.h" # include # include -# include # ifndef NSIG # define NSIG 64 diff --git a/src/subst/replace_substr.c b/src/subst/replace_substr.c index eadd56b..8ab1b92 100644 --- a/src/subst/replace_substr.c +++ b/src/subst/replace_substr.c @@ -6,7 +6,7 @@ /* By: jguelen +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/02/25 13:02:59 by jguelen #+# #+# */ -/* Updated: 2025/02/26 10:55:00 by jguelen ### ########.fr */ +/* Updated: 2025/02/26 13:30:07 by jguelen ### ########.fr */ /* */ /* ************************************************************************** */ @@ -44,88 +44,3 @@ char *replace_in_str(const char *text, size_t index_start, size_t index_end, } return (ft_strdup(text)); } - -/* -** word is presumed not to be NULL -*/ -static int *better_prefix_array(char *word, size_t word_len) -{ - int i; - int j; - int *better_prefix; - - i = 0; - better_prefix = ft_calloc(word_len + 1, sizeof(int)); - if (!better_prefix) - return (NULL); - better_prefix[0] = -1; - j = 0; - while (++j < word_len) - { - if (word[i] == word[j]) - better_prefix[j] = better_prefix[i]; - else - { - better_prefix[j] = i; - i = better_prefix[i]; - while (i >= 0 && word[j] != word[i]) - i = better_prefix[i]; - } - i++; - } - better_prefix[word_len] = i; - return (better_prefix); -} - -/* -** Return the index at which the word needle begins in haystack if found, -** -1 if needle is not to be found in haystack. -*/ -ssize_t find_word_byprefix(const char *haystack, char *needle) -{ - int i; - int j; - size_t len_n; - int *better_prefix; - - i = 0; - j = 0; - len_n = ft_strlen(needle); - better_prefix = better_prefix_array(needle, len_n); - while (haystack[j]) - { - while (i >= 0 && needle[i] != haystack[j]) - i = better_prefix[i]; - i++; - if (i == len_n) - return (free(better_prefix), j); - i = better_prefix[i]; - j++; - } - free(better_prefix); - return (-1); -} - -/* -** @RETURN Returns a new C-compliant malloc-allocated string corresponding to -** the C-compliant string text where the first occurrence of the C-compliant -** string to_replace (NULL-byte excluded) found in text is replaced by the -** C-compliant string replacement. -** Returns NULL if an allocation error occurred. -*/ -char *replace_substr(const char *text, char *to_replace, char *replacement) -{ - char *new; - size_t start_index; - size_t len_torep; - - len_torep = ft_strlen(to_replace); - if (len_torep) - { - start_index = find_word_index(text, to_replace); - if (start_index != -1) - return (replace_in_str(text, start_index, - (size_t)start_index + len_torep - 1, replacement)); - } - return (ft_strdup(text)); -} diff --git a/src/subst/replace_substr.h b/src/subst/replace_substr.h index 77c7294..343b25c 100644 --- a/src/subst/replace_substr.h +++ b/src/subst/replace_substr.h @@ -6,13 +6,11 @@ /* By: jguelen +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/02/25 12:48:39 by jguelen #+# #+# */ -/* Updated: 2025/02/26 10:56:30 by jguelen ### ########.fr */ +/* Updated: 2025/02/26 13:30:18 by jguelen ### ########.fr */ /* */ /* ************************************************************************** */ #include "libft.h" -ssize_t find_word_byprefix(const char *haystack, char *needle); char *replace_in_str(const char *text, size_t index_start, size_t index_end, char *replacement); -char *replace_substr(const char *text, char *to_replace, char *replacement); diff --git a/src/subst/subst.h b/src/subst/subst.h new file mode 100644 index 0000000..e92130e --- /dev/null +++ b/src/subst/subst.h @@ -0,0 +1,24 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* subst.h :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/23 15:01:40 by jguelen #+# #+# */ +/* Updated: 2025/02/28 13:43:23 by jguelen ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#ifndef SUBST_H +# define SUBST_H + +# include +# include "../parser/wordlist/wordlist.h" +# include "../minishell.h" + +int expand_question_mark(t_minishell *app); +t_wordlist *wordlist_var_expansion(t_wordlist *list, t_env *env); +t_wordlist *expand_star(char *file_pattern); + +#endif diff --git a/src/subst/variable_subst.c b/src/subst/variable_subst.c new file mode 100644 index 0000000..f3bfe66 --- /dev/null +++ b/src/subst/variable_subst.c @@ -0,0 +1,138 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* variable_subst.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/23 15:02:37 by jguelen #+# #+# */ +/* Updated: 2025/02/28 18:48:15 by jguelen ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "subst.h" +#include "replace_substr.h" +#include "../env/env.h" +#include "../env/env_manip.h" + +/* +** @RETURN Returns a malloc-allocated string representing the return value of +** the last foreground executed pipeline. +*/ +char *expand_question_mark(t_minishell *app) +{ + return (ft_itoa(app->last_return_value)); +} + +/* +** Returns the longest possible substring present in str starting at the +** beginning that follows the definition of a valid bash identifier. +** NOTE(reminder): a valid bash identifier (name) is a non-empty string +** starting with a '_' or an alphabetic character and composed only of '_'s or +** alphanumerical characters. +** The returned string is malloc-allocated. +** In case of an allocation error, NULL is returned. +*/ +char *ft_get_longest_identifier(char *str) +{ + char *key; + size_t i; + + if (!str || (*str != '_' && !ft_isalpha(*str))) + return (ft_strdup("")); + i = 1; + while (str[i]) + { + if (!ft_isalpha(str[i]) && !ft_isdigit(str[i]) && str[i] != '_') + break ; + i++; + } + key = malloc((i + 1)* sizeof(char)); + if (!key) + return (NULL); + ft_memcpy(key, str, i); + key[i] = '\0'; + return (key); +} + +/* +** Replace the word field of the t_worddesc pointer word with a new string +** corresponding to the replacement of the '$' followed by a valid bash +** identifier with the corresponding value if present in env or nothing if +** the corresponding identifier does not have an entry in env. +** Additionnally if the first character following is a digit both the '$' +** and digit are removed even though we do not deal with those type of bash +** parameters (we consider them to never hold a value). +** Similarly if the character following the '$' is neither '_' or +** alphanumerical the dollar is removed except for the appearance of a '\0' +** signaling the end of the word. +** Returns NULL in case of error. +*/ +t_worddesc *word_var_expansion(t_worddesc *word, t_env *env) +{ + size_t i; + char *id; + char *tmp; + char *rep; + + i = 0; + while (((*word)->word)[i]) + { + if (word->flags[i] != '\'' && word->word[i] == '$') + { + id = ft_get_longest_identifier((word->word) + i + 1); + if (!id) + return (NULL); + id_len = ft_strlen(id); + if ((id_len == 0 && (word->word[i + 1] == '\0' + || word->word[i + 1] == '"' || word->word[i + 1] == '\'')) + || (id_len == 1 && ft_isdigit(*id))) + { + free(id); + if (word->word[i + 1] == '\0') + continue ; + tmp = replace_in_str(word->word, i, i, NULL); + free(word->word); + word->word = tmp; + /////flags update + continue ; + } + rep = env_get_val(env, id); + free(id); + tmp = replace_in_str(word->word, i, i + id_len, rep); + free(rep); + if (!tmp) + return (NULL); + free(word->word); + word->word = tmp; + ////flags update + } + i++; + } + return (*word); +} + +/* +** Returns the t_wordlist passed as a parameter where the words have been +** modified to contain string that represent the result of parameter expansion +** where the introductory '$' character was not single quoted. +** We do NOT take the '\' character into account as an escape character here +** under any circumstance per subject requirement. +*/ +t_wordlist *wordlist_var_expansion(t_wordlist *list, t_env *env) +{ + t_wordlist *tmp_list; + char *tmp_word; + ssize_t idx_start; + char *value; + + tmp_list = list; + while (tmp_list) + { + if ((tmp_list->word->flags & W_HASDOLLAR) + && word_var_expansion(tmp_list->word, env) == NULL) + return (NULL); + tmp_list = tmp_list->next; + } + return (list); +} diff --git a/src/subst/wildcard_exp.c b/src/subst/wildcard_exp.c new file mode 100644 index 0000000..289a23a --- /dev/null +++ b/src/subst/wildcard_exp.c @@ -0,0 +1,29 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* wildcard_exp.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/23 15:02:59 by jguelen #+# #+# */ +/* Updated: 2025/02/25 10:07:59 by jguelen ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "subst.h" + +/* +** TODO +*/ + +/* +** A function designed to present all possible * filename expansions for the +** current directory. +** Does not take into account any other wildcard and does only search the +** current working directory. +** @PARAM A C compliant character string representing a pattern for a filename. +** @RETURN A NULL-terminated pointer to a wordlist +*/ +t_wordlist *expand_star(char *file_pattern) +{ +} diff --git a/tests/Makefile b/tests/Makefile index 0bffde6..54dc2cb 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -6,6 +6,7 @@ rawtests = \ test_redirection_parsing \ quote_removal \ cmdlist_use_after_free \ + expansion \ metacharacters \ parse_command_lists \ parse_pipelines \ diff --git a/tests/expansion.c b/tests/expansion.c new file mode 100644 index 0000000..0ea80fd --- /dev/null +++ b/tests/expansion.c @@ -0,0 +1,109 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* expansion.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: jguelen +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2025/02/23 15:00:18 by jguelen #+# #+# */ +/* Updated: 2025/02/28 14:23:30 by jguelen ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include +#include +#include "testutil.h" +#include "../src/subst/subst.h" +#include "../src/subst/replace_substr.h" +#include "../../env/env.h" +#include "../../env/env_manip.h" + +/* +** Test file for the different expansion/substitution types of minishell. +*/ + +static void test_insert_instr(void) +{ + char *line; + + line = replace_in_str("abcdefghijk", 1, 4, "souris"); + assert_strequal("asourisfghijk", line); + free(line); + line = replace_in_str("abcdefgh" , 2, 2, "non ce n'est pas ma faute"); + assert_strequal("abnon ce n'est pas ma fautedefgh", line); + free(line); + line = replace_in_str("le petit canari qui fait cuicui", 3, 8, ""); + assert_strequal("le canari qui fait cuicui", line); + free(line); + line = replace_in_str("le petit canari qui fait cuicui", 3, 8, NULL); + assert_strequal("le canari qui fait cuicui", line); + free(line); + line = replace_in_str("le canari qui fait cuicui", 2, 2, " petit "); + assert_strequal("le petit canari qui fait cuicui", line); + free(line); +} + +static void test_env_variable_expansion(void) +{ + char *token; + char *tk; + t_env *env; + + token = ft_strdup("$USER"); + if (!token) + assert("ft_strdup failed" && false); + env = NULL; + env = env_set_entry(&env, "USER", "jguelen"); + if (env == NULL) + assert("malloc failed" && false); + env = env_set_entry(&env, "_canard", "coing coing"); + if (!env) + assert("malloc failed: slipped on a duck" && false); + tk = word_var_expansion(&token, env); + if (!tk) + assert("internal word_var_expansion failure" && false); + assert_strequal("jguelen", tk); + free(token); + token = ft_strdup("\"$_caneton\""); + if (!token) + assert("ft_strdup failed" && false); + tk = word_var_expansion(&token, env); + if (!tk) + assert("internal word_var_expansion failure" && false); + assert_strequal("\"\"", tk); + free(token); + token = ft_strdup("$_canard$USER$''$USER\"\"\"$_canard\"$"); + if (!token) + assert("ft_strdup failed" && false); + tk = word_var_expansion(token, env); + if (!tk) + assert("internal word_var_expansion failure" && false); + assert_strequal("coing coingjguelen''jguelencoing coing$", tk); + free(token); + token = ft_strdup("$_can'a'rd"); + if (!token) + assert("ft_strdup failed" && false); + tk = word_var_expansion(token, env); + if (!tk) + assert("internal word_var_expansion failure" && false); + assert_strequal("'a'rd", tk); + free(token); + env_destroy(env); +} + +static void test_filename_path_expansion(void) +{ +} + +static void test_filename_star_expansion(void) +{ +} + +int main(void) +{ + test_insert_instr(); + test_env_variable_expansion(); + test_filename_path_expansion(); + test_filename_star_expansion(); + return (0); +}