/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* variable_subst.c :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: khais +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/03/19 17:28/29 by khais #+# #+# */ /* Updated: 2025/03/24 10:52:10 by jguelen ### ########.fr */ /* */ /* ************************************************************************** */ #include "subst.h" #include "../minishell.h" #include "replace_substr.h" #include "../env/env_manip.h" /* ** @Param ** word: the worddesc to be modified ** i: the index at which the $ marking the start of expansion is located ** id_len: the length of the string that constitutes a valid bash ** identifier (it is to be noted that it can be zero and yet still ** conduct to a replacement of more that just $ if $ is immediately ** followed by a digit -- this cans be modified as it is not strictly ** required to consider positionnal arguments at all or indeed treat ** them as holding no value) ** rep: a malloc-allocated string previously calculated to correspond to ** the actual value to set as the expansion ** This function exists to actually modify the worddesc word by modifying the ** word proper inside but also keep the marker string coherent with these ** modifications so that further steps can act properly with the type of ** expansion and properly field split (word split in the manual) the result or ** not. To this end if an expansion occurs outside quotes every character ** resulting from it is marked simply with a '$' character and is subject to ** future field splitting. If however, the expansion occurs within double quotes ** it is not to be subjected to field splitting in the future and every ** character resulting from such an expansion is marked with a '&'. ** ** cf. https://www.gnu.org/software/bash/manual/bash.html#Quoting ** section 3.5.7 ** The shell scans the results of parameter expansion, command substitution, and ** arithmetic expansion that did not occur within double quotes for word ** splitting. ** ** NOTE: It frees the malloc-allocated string rep. ** ** @RETURN In case of allocation error returns NULL, oterwise returns word ** itself. */ static t_worddesc *word_update(t_worddesc *word, size_t i, size_t id_len, char *rep) { char *new_word; size_t rep_len; size_t digit; digit = ft_isdigit(word->word[i + 1]); rep_len = ft_strlen(rep); new_word = replace_in_str(word->word, i, i + id_len + digit, rep); free(rep); free(word->word); word->word = new_word; if (word->marker[i] == '"') rep = construct_repeting_char_string('&', rep_len); else rep = construct_repeting_char_string('$', rep_len); if (!rep) return (NULL); new_word = replace_in_str(word->marker, i, i + id_len + digit, rep); free(rep); if (!new_word) return (NULL); free(word->marker); word->marker = new_word; return (word); } /* ** Calculates the string corresponding to the value of the variable to be ** expanded in the word proper and returns it. ** The string returned is always malloc-allocated or NULL. */ static char *calculate_replacement(t_worddesc *word, t_minishell *app, size_t i, size_t *id_len) { char *rep; char *id; 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] == '?') { *id_len = 1; rep = expand_question_mark(app); if (!rep) return (NULL); } else { rep = env_get_val(app->env, id); if (!rep) rep = ft_strdup(""); else rep = ft_strdup(rep); if (!rep) return (NULL); } return (free(id), rep); } /* ** @Param word should NOT be NULL. ** 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, the return value ** of the last foreground executed pipeline in the case of $? 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, or the worddesc pointer word itself if ** everything went normally. */ t_worddesc *word_var_expansion(t_worddesc *word, t_minishell *app) { size_t i; size_t id_len; size_t rep_len; char *rep; i = 0; while (word->word[i] && word->word[i + 1]) { rep_len = 1; if (word->marker[i] != '\'' && word->word[i] == '$') { rep = calculate_replacement(word, app, i, &id_len); if (!rep) return (NULL); rep_len = ft_strlen(rep); if (word_update(word, i, id_len, rep) == NULL) return (NULL); } i += rep_len; } return (word); } /* ** Returns the t_wordlist passed as a parameter where the words have been ** modified to contain strings 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_minishell *app) { t_wordlist *tmp_list; tmp_list = list; while (tmp_list) { if ((tmp_list->word->flags & W_HASDOLLAR) && word_var_expansion(tmp_list->word, app) == NULL) return (NULL); tmp_list = tmp_list->next; } return (list); }