/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* parser.c :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: sede-san next == NULL) syntax_error_unexpected_token((t_token *)current_token->content); current_token = current_token->next; } } return (commands); } static void expand_variable( char **argument, int *i, t_minishell *minishell ) { char *expanded; char *variable; char *variable_name; 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 = get_env(variable_name, minishell); free(variable_name); if (variable == NULL) variable = ""; expanded = ft_strnjoin(3, ft_substr(*argument, 0, *i), variable, ft_substr(*argument,end, ft_strlen(*argument) - end)); if (expanded == NULL) return (minishell->exit = true, malloc_error()); *i += ft_strlen(variable) - 1; 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); }