minishell/src/subst/variable_subst.c

176 lines
6 KiB
C
Raw Normal View History

/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* variable_subst.c :+: :+: :+: */
/* +:+ +:+ +:+ */
2025-03-19 17:58:46 +01:00
/* By: khais <marvin@42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
2025-03-19 17:58:46 +01:00
/* 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"
2025-03-08 17:32:47 +01:00
/*
** @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;
2025-03-08 17:32:47 +01:00
if (word->marker[i] == '"')
rep = construct_repeting_char_string('&', rep_len);
else
rep = construct_repeting_char_string('$', rep_len);
if (!rep)
return (NULL);
2025-03-08 17:32:47 +01:00
new_word = replace_in_str(word->marker, i, i + id_len + digit, rep);
free(rep);
if (!new_word)
return (NULL);
2025-03-08 17:32:47 +01:00
free(word->marker);
word->marker = new_word;
return (word);
}
2025-03-08 17:32:47 +01:00
/*
** 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.
2025-03-08 17:32:47 +01:00
*/
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)
2025-03-21 18:54:10 +01:00
rep = ft_strdup("");
else
2025-03-21 18:54:10 +01:00
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])
{
2025-03-19 17:58:46 +01:00
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
2025-03-08 17:32:47 +01:00
** modified to contain strings that represent the result of parameter expansion
** where the introductory '$' character was not single quoted.
2025-03-08 17:32:47 +01:00
** 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);
}