diff --git a/include/core.h b/include/core.h index a9717fc..68c5e61 100644 --- a/include/core.h +++ b/include/core.h @@ -72,7 +72,6 @@ typedef struct s_prompt const char *ps2; // secondary prompt string for multiline input } t_prompt; - /** * @brief Main minishell structure containing global state information * diff --git a/include/errors.h b/include/errors.h index 39ec8fc..3742387 100644 --- a/include/errors.h +++ b/include/errors.h @@ -17,7 +17,7 @@ # include "core.h" # include "parser.h" -extern void syntax_error_unexpected_token(t_token *token); +extern void syntax_error_unexpected_token(t_token *token); extern void malloc_error(void); #endif /* ERRORS_H */ \ No newline at end of file diff --git a/include/minishell.h b/include/minishell.h index db657eb..f76bea5 100644 --- a/include/minishell.h +++ b/include/minishell.h @@ -42,4 +42,7 @@ # include // tgetent(3), tgetflag(3), tgetnum(3), // tgetstr(3), tgoto(3), tputs(3) +void handle_sigint_status(t_minishell *minishell); +bool handle_eof(char *line, t_minishell *minishell); + #endif /* MINISHELL_H */ diff --git a/include/parser.h b/include/parser.h index 2ce438e..469a80f 100644 --- a/include/parser.h +++ b/include/parser.h @@ -23,19 +23,29 @@ // parser.c -extern t_list *parse(char *line, t_minishell *minishell); +extern t_list *parse(char *line, t_minishell *minishell); // lexer.c -extern t_list *lex(const char *line); +t_token_type get_token_type(const char *str); +t_token *token_new(t_token_type type, char *text); +t_token *read_token(t_token_type type, const char *line, + size_t *i); +t_token *read_word(const char *line, size_t *i); -extern void token_clear(t_token *token); +extern t_list *lex(const char *line); + +extern void token_clear(t_token *token); extern t_command *command_new(t_list **tokens); extern void command_clear(t_command *command); extern void command_add_tokens(t_command **command, t_list **tokens); extern bool is_pipe(t_token *token); -extern bool is_redirection(t_token *token); -void redirection_add(t_list **tokens, t_token *token, t_command **command); -void words_add(t_list **tokens, t_command **command); +extern bool is_redirection(t_token *token); +void redirection_add(t_list **tokens, t_token *token, + t_command **command); +void words_add(t_list **tokens, t_command **command); + +void expand(t_list **commands, t_minishell *minishell); +void redirection_clear(t_redirection *redirection); #endif /* PARSER_H */ diff --git a/src/core/command/command.c b/src/core/command/command.c index 0a9250f..a119b6a 100644 --- a/src/core/command/command.c +++ b/src/core/command/command.c @@ -9,4 +9,3 @@ /* Updated: 2026/02/09 18:40:04 by sede-san ### ########.fr */ /* */ /* ************************************************************************** */ - diff --git a/src/errors/errors.c b/src/errors/errors.c index b8d8238..dea4162 100644 --- a/src/errors/errors.c +++ b/src/errors/errors.c @@ -28,4 +28,4 @@ void syntax_error_unexpected_token( void malloc_error(void) { ft_eprintf("minishell: %s\n", strerror(ENOMEM)); -} \ No newline at end of file +} diff --git a/src/minishell.c b/src/minishell.c index 76f7920..909c116 100644 --- a/src/minishell.c +++ b/src/minishell.c @@ -15,28 +15,6 @@ #include "parser.h" #include "executor.h" -static void handle_sigint_status( - t_minishell *minishell -) -{ - if (!minishell_consume_sigint()) - return ; - minishell->exit_status = 130; -} - -static bool handle_eof( - char *line, - t_minishell *minishell -) -{ - if (line != NULL) - return (false); - if (isatty(STDIN_FILENO)) - ft_putendl("exit"); - minishell->exit = true; - return (true); -} - static void set_prompts( t_minishell *minishell ) diff --git a/src/minishell_helpers.c b/src/minishell_helpers.c new file mode 100644 index 0000000..2404b4a --- /dev/null +++ b/src/minishell_helpers.c @@ -0,0 +1,36 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* minishell_helpers.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san exit_status = 130; +} + +bool handle_eof( + char *line, + t_minishell *minishell +) +{ + if (line != NULL) + return (false); + if (isatty(STDIN_FILENO)) + ft_putendl("exit"); + minishell->exit = true; + return (true); +} diff --git a/src/parser/lexer.c b/src/parser/lexer.c index 3ebeb63..265bcb4 100644 --- a/src/parser/lexer.c +++ b/src/parser/lexer.c @@ -13,26 +13,20 @@ #include "core.h" #include "parser.h" -static t_token *tokenize(const char *line, size_t *start); -static t_token_type get_token_type(const char *str); -static t_token *token_new(t_token_type type, char *text); -void token_clear(t_token *token); -static t_token *read_token(t_token_type type, const char *line, size_t *i); -static t_token *read_word(const char *line, size_t *i); -static inline bool is_meta(char c); +static t_token *tokenize(const char *line, size_t *start); /** * @brief Converts a command line string into a list of tokens. * * @return A list of tokens or NULL on error. */ -t_list *lex( +t_list *lex( const char *line ) { - t_list *tokens; - t_token *token; - size_t i; + t_list *tokens; + t_token *token; + size_t i; tokens = NULL; i = 0; @@ -56,13 +50,13 @@ t_list *lex( /** * @return A new token or NULL on error. */ -static t_token *tokenize( +static t_token *tokenize( const char *line, size_t *start ) { - t_token *token; - t_token_type type; + t_token *token; + t_token_type type; if (line == NULL || line[*start] == '\0') return (NULL); @@ -73,106 +67,3 @@ static t_token *tokenize( token = read_word(line, start); return (token); } - -static t_token_type get_token_type( - const char *str -) -{ - if (str == NULL || str[0] == '\0') - return (TOKEN_WORD); - if (str[0] == '|') - return (TOKEN_PIPE); - if (str[0] == '<') - { - if (str[1] == '<') - return (TOKEN_HEREDOC); - return (TOKEN_REDIRECT_IN); - } - if (str[0] == '>') - { - if (str[1] == '>') - return (TOKEN_APPEND); - return (TOKEN_REDIRECT_OUT); - } - return (TOKEN_WORD); -} - -static t_token *token_new( - t_token_type type, - char *text -) -{ - t_token *token; - - token = (t_token *)malloc(sizeof(t_token)); - if (token == NULL) - return (NULL); - token->type = type; - token->value = text; - if (token->type == TOKEN_WORD && token->value == NULL) - { - free(token); - return (NULL); - } - return (token); -} - -void token_clear( - t_token *token -) -{ - if (token != NULL) - { - free(token->value); - free(token); - } -} - -static t_token *read_token( - t_token_type type, - const char *line, - size_t *i -) -{ - const size_t start = *i; - size_t end; - - while (is_meta(line[*i])) - (*i)++; - end = *i; - while (ft_isspace(line[*i])) - (*i)++; - return (token_new(type, ft_substr(line, start, end - start))); -} - -static t_token *read_word( - const char *line, - size_t *i -) -{ - const size_t start = *i; - bool in_single_quote; - bool in_double_quote; - - in_single_quote = false; - in_double_quote = false; - while (line[*i] != '\0') - { - if (line[*i] == '\'' && !in_double_quote) - in_single_quote = !in_single_quote; - else if (line[*i] == '"' && !in_single_quote) - in_double_quote = !in_double_quote; - else if (!in_single_quote && !in_double_quote - && (isspace(line[*i]) || is_meta(line[*i]))) - break; - (*i)++; - } - return (token_new(TOKEN_WORD, ft_substr(line, start, *i - start))); -} - -static inline bool is_meta( - char c -) -{ - return (c == '|' || c == '<' || c == '>'); -} diff --git a/src/parser/lexer_reader.c b/src/parser/lexer_reader.c new file mode 100644 index 0000000..91ed2ea --- /dev/null +++ b/src/parser/lexer_reader.c @@ -0,0 +1,64 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* lexer_reader.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san '); +} diff --git a/src/parser/lexer_token.c b/src/parser/lexer_token.c new file mode 100644 index 0000000..33754e9 --- /dev/null +++ b/src/parser/lexer_token.c @@ -0,0 +1,67 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* lexer_token.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san ') + { + if (str[1] == '>') + return (TOKEN_APPEND); + return (TOKEN_REDIRECT_OUT); + } + return (TOKEN_WORD); +} + +t_token *token_new( + t_token_type type, + char *text +) +{ + t_token *token; + + token = (t_token *)malloc(sizeof(t_token)); + if (token == NULL) + return (NULL); + token->type = type; + token->value = text; + if (token->type == TOKEN_WORD && token->value == NULL) + { + free(token); + return (NULL); + } + return (token); +} + +void token_clear( + t_token *token +) +{ + if (token != NULL) + { + free(token->value); + free(token); + } +} diff --git a/src/parser/parser.c b/src/parser/parser.c index a40af4b..b338e28 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -6,33 +6,14 @@ /* By: sede-san content)->type == TOKEN_PIPE) - return (syntax_error_unexpected_token((t_token *)current_token->content), NULL); + return (syntax_error_unexpected_token( + (t_token *)current_token->content), NULL); while (current_token != NULL) { command = command_new(¤t_token); @@ -103,404 +84,11 @@ static t_list *parse_tokens( if (current_token->next == NULL) { ft_lstclear(&commands, (void (*)(void *))command_clear); - return (syntax_error_unexpected_token((t_token *)current_token->content), NULL); + return (syntax_error_unexpected_token( + (t_token *)current_token->content), NULL); } current_token = current_token->next; } } return (commands); } - -static char *replace_value( - char *original, - char *value, - int start, - int end -) -{ - const char *before = ft_substr(original, 0, start); - const char *after = ft_substr(original, end, ft_strlen(original) - end); - const char *expanded = ft_strnjoin(3, before, value, after); - - free((char *)before); - free((char *)after); - return ((char *)expanded); -} - -static void expand_variable( - char **argument, - int *i, - t_minishell *minishell -) -{ - char *expanded; - char *variable_name; - char *variable_value; - const int start = *i + 1; - int end; - - end = start; - while (ft_isalnum((*argument)[end]) || (*argument)[end] == '_') - end++; - variable_name = ft_substr(*argument, start, end - start); - if (variable_name == NULL) - return (minishell->exit = true, malloc_error()); - variable_value = get_env(variable_name, minishell); - free(variable_name); - if (variable_value == NULL) - variable_value = ""; - expanded = replace_value(*argument, variable_value, start - 1, end); - if (expanded == NULL) - return (minishell->exit = true, malloc_error()); - *i += ft_strlen(variable_value); - free(*argument); - *argument = expanded; -} - -static void expand_argument( - char **argument, - t_minishell *minishell -) -{ - bool in_single_quote; - bool in_double_quote; - int i; - - in_single_quote = false; - in_double_quote = false; - i = 0; - while ((*argument)[i] != '\0') - { - if ((*argument)[i] == '$' && !in_single_quote) - { - expand_variable(argument, &i, minishell); - if (*argument == NULL) - return (minishell->exit = true, malloc_error()); - } - else - { - if ((*argument)[i] == '\'' && !in_double_quote) - in_single_quote = !in_single_quote; - else if ((*argument)[i] == '"' && !in_single_quote) - in_double_quote = !in_double_quote; - i++; - } - } -} - -static void expand( - t_list **commands, - t_minishell *minishell -) -{ - t_list *current_command; - t_command *command; - int i; - - current_command = *commands; - while (current_command != NULL) - { - command = (t_command *)current_command->content; - i = 0; - while (i < command->argc) - { - expand_argument(&command->argv[i], minishell); - if (command->argv[i] == NULL) - ft_lstclear(commands, (void (*)(void *))command_clear); - i++; - } - if (command == NULL) - return (ft_lstclear(commands, (void (*)(void *))command_clear)); - current_command = current_command->next; - } -} - -/** - * @brief Creates a new command from a list of tokens. - * - * @param tokens The list of tokens to create the command from. - * - * @return A new command or NULL on error. - * - * @note The `tokens` pointer is moved to the next command's tokens. - */ -t_command *command_new( - t_list **tokens -) -{ - t_command *command; - t_list *current_token; - t_list *delimiter_token; - - command = (t_command *)ft_calloc(1, sizeof(t_command)); - if (command == NULL) - return (NULL); - current_token = *tokens; - delimiter_token = ft_lstfind(current_token, (bool (*)(void *))is_pipe); - while (command != NULL && current_token != delimiter_token) - command_add_tokens(&command, ¤t_token); - *tokens = current_token; - return (command); -} - -/** - * @brief Creates a new redirection from a list of tokens. - * - * @param tokens The list of tokens to create the redirection from. - * - * @return A new redirection or `NULL` on error. - */ -t_redirection *redirection_new( - t_list **tokens -) -{ - t_redirection *redirection; - t_token *token; - - redirection = (t_redirection *)malloc(sizeof(t_redirection)); - if (redirection == NULL) - return (malloc_error(), NULL); - token = (t_token *)(*tokens)->content; - redirection->type = token->type; - *tokens = (*tokens)->next; - if (*tokens == NULL) - { - free(redirection); - return (syntax_error_unexpected_token(NULL), NULL); - } - token = (t_token *)(*tokens)->content; - if (token->type != TOKEN_WORD) - { - free(redirection); - while (*tokens != NULL) - *tokens = (*tokens)->next; - return (syntax_error_unexpected_token(token), NULL); - } - redirection->target = ft_strdup(token->value); - if (redirection->target == NULL) - { - free(redirection); - return (malloc_error(), NULL); - } - *tokens = (*tokens)->next; - return (redirection); -} - -void redirection_clear( - t_redirection *redirection -) -{ - if (redirection != NULL) - { - free(redirection->target); - free(redirection); - } -} - -/** - * @brief Adds a token to a command, updating the command's arguments and - * redirections as necessary. - * - * @param command The command to add the token to. - * @param tokens The list of tokens to add to the command. - * - * @note The `command` pointer can be free'd if there is an error while adding - * the token. - */ -void command_add_tokens( - t_command **command, - t_list **tokens -) -{ - t_token *token; - - token = (t_token *)(*tokens)->content; - if (is_redirection(token)) - redirection_add(tokens, token, command); - else - words_add(tokens, command); -} - -/** - * @brief Converts a list of arguments into an array. - * - * @param args The list of arguments to convert. - * @param argc The number of arguments in the list. - * - * @return An array of arguments or `NULL` on error. - * - * @note The `args` list is cleared after the conversion and set to NULL. - */ -char **args_to_array( - t_list **args, - size_t argc -) -{ - char **argv; - t_list *arg; - size_t i; - - argv = (char **)malloc(sizeof(char *) * (argc + 1)); - if (argv == NULL) - return (NULL); - i = 0; - arg = *args; - while (arg != NULL) - { - argv[i] = (char *)arg->content; - arg = arg->next; - i++; - } - argv[i] = NULL; - ft_lstclear_nodes(args); - return (argv); -} - -/** - * @brief Adds all consecutive word tokens to a command's argv and updates its - * argc accordingly. - * - * @param command The command to add the word tokens to. - * @param tokens The list of tokens to add to the command. - */ -void words_add( - t_list **tokens, - t_command **command -) -{ - t_list *args; - t_list *arg; - t_token *token; - - args = NULL; - arg = *tokens; - token = (t_token *)arg->content; - while (arg != NULL && token->type == TOKEN_WORD) - { - ft_lstadd_back(&args, ft_lstnew(ft_strdup(token->value))); - (*command)->argc++; - arg = arg->next; - if (arg != NULL) - token = (t_token *)arg->content; - } - *tokens = arg; - (*command)->argv = args_to_array(&args, (*command)->argc); - ft_lstclear_nodes(&args); -} - -void redirection_add( - t_list **tokens, - t_token *token, - t_command **command -) -{ - t_redirection *redirection; - t_list *redirection_tokens; - - redirection = redirection_new(tokens); - if (redirection == NULL) - { - command_clear(*command); - *command = NULL; - return ; - } - redirection_tokens = ft_lstnew(redirection); - if (redirection_tokens == NULL) - { - free(redirection); - return (malloc_error()); - } - if (token->type == TOKEN_HEREDOC) - ft_lstadd_back(&(*command)->heredocs, redirection_tokens); - else - ft_lstadd_back(&(*command)->redirections, redirection_tokens); -} - -/** - * @brief Checks if a token is a redirection token. - * - * @param token The token to check. - * - * @return `true` if the token is a redirection token, `false` otherwise. - */ -bool is_redirection( - t_token *token -) -{ - return (token->type == TOKEN_REDIRECT_IN - || token->type == TOKEN_REDIRECT_OUT - || token->type == TOKEN_APPEND - || token->type == TOKEN_HEREDOC); -} - -void command_clear_argv( - t_command *command -) -{ - int i; - - if (command->argv != NULL) - { - i = 0; - while (i < command->argc) - { - free(command->argv[i]); - i++; - } - free(command->argv); - command->argv = NULL; - } -} - -/** - * @brief Clears a command, freeing all associated memory. - * - * @param command The command to clear. - */ -void command_clear( - t_command *command -) -{ - if (command != NULL) - { - command_clear_argv(command); - ft_lstclear(&command->redirections, (void (*)(void *))redirection_clear); - ft_lstclear(&command->heredocs, (void (*)(void *))redirection_clear); - free(command); - } -} - -/** - * @brief Checks if a token is a pipe token. - * - * @param token The token to check. - * - * @return `true` if the token is a pipe token, `false` otherwise. - */ -bool is_pipe( - t_token *token) -{ - return (token->type == TOKEN_PIPE); -} - -/** - * @brief Finds a node in a linked list that satisfies a given predicate. - * - * @param lst The linked list to search through. - * @param pre The predicate function to apply to each node's content. - * - * @returns The first node that satisfies the predicate or `NULL` if no such - * node exists or if the list is `NULL`. - */ -t_list *ft_lstfind( - t_list *lst, - bool (*pre)(void *)) -{ - while (lst != NULL) - { - if (pre(lst->content)) - return (lst); - lst = lst->next; - } - return (NULL); -} diff --git a/src/parser/parser_command.c b/src/parser/parser_command.c new file mode 100644 index 0000000..66171db --- /dev/null +++ b/src/parser/parser_command.c @@ -0,0 +1,120 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parser_command.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san content; + if (is_redirection(token)) + redirection_add(tokens, token, command); + else + words_add(tokens, command); +} + +/** + * @brief Checks if a token is a redirection token. + * + * @param token The token to check. + * + * @return `true` if the token is a redirection token, `false` otherwise. + */ +bool is_redirection( + t_token *token +) +{ + return (token->type == TOKEN_REDIRECT_IN + || token->type == TOKEN_REDIRECT_OUT + || token->type == TOKEN_APPEND + || token->type == TOKEN_HEREDOC); +} + +/** + * @brief Checks if a token is a pipe token. + * + * @param token The token to check. + * + * @return `true` if the token is a pipe token, `false` otherwise. + */ +bool is_pipe( + t_token *token +) +{ + return (token->type == TOKEN_PIPE); +} + +/** + * @brief Finds a node in a linked list that satisfies a given predicate. + * + * @param lst The linked list to search through. + * @param pre The predicate function to apply to each node's content. + * + * @returns The first node that satisfies the predicate or `NULL` if no such + * node exists or if the list is `NULL`. + */ +static t_list *ft_lstfind( + t_list *lst, + bool (*pre)(void *)) +{ + while (lst != NULL) + { + if (pre(lst->content)) + return (lst); + lst = lst->next; + } + return (NULL); +} diff --git a/src/parser/parser_expand.c b/src/parser/parser_expand.c new file mode 100644 index 0000000..1ef4782 --- /dev/null +++ b/src/parser/parser_expand.c @@ -0,0 +1,122 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parser_expand.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san exit = true, malloc_error()); + variable_value = get_env(variable_name, minishell); + free(variable_name); + if (variable_value == NULL) + variable_value = ""; + expanded = replace_value(*argument, variable_value, start - 1, end); + if (expanded == NULL) + return (minishell->exit = true, malloc_error()); + *i += ft_strlen(variable_value); + free(*argument); + *argument = expanded; +} + +static void expand_argument( + char **argument, + t_minishell *minishell +) +{ + bool in_single_quote; + bool in_double_quote; + int i; + + in_single_quote = false; + in_double_quote = false; + i = 0; + while ((*argument)[i] != '\0') + { + if ((*argument)[i] == '$' && !in_single_quote) + { + expand_variable(argument, &i, minishell); + if (*argument == NULL) + return (minishell->exit = true, malloc_error()); + } + else + { + if ((*argument)[i] == '\'' && !in_double_quote) + in_single_quote = !in_single_quote; + else if ((*argument)[i] == '"' && !in_single_quote) + in_double_quote = !in_double_quote; + i++; + } + } +} + +void expand( + t_list **commands, + t_minishell *minishell +) +{ + t_list *current_command; + t_command *command; + int i; + + current_command = *commands; + while (current_command != NULL) + { + command = (t_command *)current_command->content; + i = 0; + while (i < command->argc) + { + expand_argument(&command->argv[i], minishell); + if (command->argv[i] == NULL) + ft_lstclear(commands, (void (*)(void *))command_clear); + i++; + } + if (command == NULL) + return (ft_lstclear(commands, (void (*)(void *))command_clear)); + current_command = current_command->next; + } +} diff --git a/src/parser/parser_redirection.c b/src/parser/parser_redirection.c new file mode 100644 index 0000000..dcf3bb7 --- /dev/null +++ b/src/parser/parser_redirection.c @@ -0,0 +1,98 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parser_redirection.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san content; + redirection->type = token->type; + *tokens = (*tokens)->next; + if (*tokens == NULL) + { + free(redirection); + return (syntax_error_unexpected_token(NULL), NULL); + } + token = (t_token *)(*tokens)->content; + if (token->type != TOKEN_WORD) + { + free(redirection); + while (*tokens != NULL) + *tokens = (*tokens)->next; + return (syntax_error_unexpected_token(token), NULL); + } + redirection->target = ft_strdup(token->value); + if (redirection->target == NULL) + { + free(redirection); + return (malloc_error(), NULL); + } + *tokens = (*tokens)->next; + return (redirection); +} + +void redirection_clear( + t_redirection *redirection +) +{ + if (redirection != NULL) + { + free(redirection->target); + free(redirection); + } +} + +void redirection_add( + t_list **tokens, + t_token *token, + t_command **command +) +{ + t_redirection *redirection; + t_list *redirection_tokens; + + redirection = redirection_new(tokens); + if (redirection == NULL) + { + command_clear(*command); + *command = NULL; + return ; + } + redirection_tokens = ft_lstnew(redirection); + if (redirection_tokens == NULL) + { + free(redirection); + return (malloc_error()); + } + if (token->type == TOKEN_HEREDOC) + ft_lstadd_back(&(*command)->heredocs, redirection_tokens); + else + ft_lstadd_back(&(*command)->redirections, redirection_tokens); +} diff --git a/src/parser/parser_words.c b/src/parser/parser_words.c new file mode 100644 index 0000000..e589ec2 --- /dev/null +++ b/src/parser/parser_words.c @@ -0,0 +1,121 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* parser_words.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: sede-san content; + arg = arg->next; + i++; + } + argv[i] = NULL; + ft_lstclear_nodes(args); + return (argv); +} + +/** + * @brief Adds all consecutive word tokens to a command's argv and updates its + * argc accordingly. + * + * @param command The command to add the word tokens to. + * @param tokens The list of tokens to add to the command. + */ +void words_add( + t_list **tokens, + t_command **command +) +{ + t_list *args; + t_list *arg; + t_token *token; + + args = NULL; + arg = *tokens; + token = (t_token *)arg->content; + while (arg != NULL && token->type == TOKEN_WORD) + { + ft_lstadd_back(&args, ft_lstnew(ft_strdup(token->value))); + (*command)->argc++; + arg = arg->next; + if (arg != NULL) + token = (t_token *)arg->content; + } + *tokens = arg; + (*command)->argv = args_to_array(&args, (*command)->argc); + ft_lstclear_nodes(&args); +} + +void command_clear_argv( + t_command *command +) +{ + int i; + + if (command->argv != NULL) + { + i = 0; + while (i < command->argc) + { + free(command->argv[i]); + i++; + } + free(command->argv); + command->argv = NULL; + } +} + +/** + * @brief Clears a command, freeing all associated memory. + * + * @param command The command to clear. + */ +void command_clear( + t_command *command +) +{ + if (command != NULL) + { + command_clear_argv(command); + ft_lstclear(&command->redirections, + (void (*)(void *))redirection_clear); + ft_lstclear(&command->heredocs, (void (*)(void *))redirection_clear); + free(command); + } +}