422 lines
9.4 KiB
C
422 lines
9.4 KiB
C
/* ************************************************************************** */
|
|
/* */
|
|
/* ::: :::::::: */
|
|
/* parser.c :+: :+: :+: */
|
|
/* +:+ +:+ +:+ */
|
|
/* By: sede-san <sede-san@student.42madrid.com +#+ +:+ +#+ */
|
|
/* +#+#+#+#+#+ +#+ */
|
|
/* Created: 2025/10/22 18:37:38 by sede-san #+# #+# */
|
|
/* Updated: 2026/02/12 02:55:51 by sede-san ### ########.fr */
|
|
/* */
|
|
/* ************************************************************************** */
|
|
|
|
#include "parser.h"
|
|
#include "errors.h"
|
|
|
|
static t_list *parse_tokens(
|
|
t_list *tokens
|
|
);
|
|
|
|
static t_list *ft_lstfind(
|
|
t_list *lst,
|
|
bool (*pre)(void *)
|
|
);
|
|
|
|
/**
|
|
* @brief Converts a command line string into a list of commands.
|
|
*
|
|
* @param line The command line string to parse.
|
|
* @param minishell The minishell instance.
|
|
*
|
|
* @return A list of commands or `NULL` on error.
|
|
*/
|
|
t_list *parse(
|
|
char *line,
|
|
t_minishell *minishell
|
|
)
|
|
{
|
|
t_list *commands;
|
|
t_list *tokens;
|
|
|
|
if (line == NULL)
|
|
return (NULL);
|
|
tokens = lex(line);
|
|
commands = parse_tokens(tokens);
|
|
(void)minishell;
|
|
// expand_variables(commands, minishell);
|
|
ft_lstclear(&tokens, (void (*)(void *))token_clear);
|
|
return (commands);
|
|
}
|
|
|
|
/**
|
|
* @brief Converts a list of tokens into a list of commands.
|
|
*
|
|
* @param tokens The list of tokens to parse.
|
|
* @param minishell The minishell instance.
|
|
*
|
|
* @return A list of commands or `NULL` on error.
|
|
*/
|
|
static t_list *parse_tokens(
|
|
t_list *tokens
|
|
)
|
|
{
|
|
t_list *commands;
|
|
t_command *command;
|
|
t_list *current_token;
|
|
t_list *new_command;
|
|
|
|
if (tokens == NULL)
|
|
return (NULL);
|
|
commands = NULL;
|
|
current_token = tokens;
|
|
while (current_token != NULL)
|
|
{
|
|
command = command_new(¤t_token);
|
|
if (command == NULL)
|
|
{
|
|
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
|
return (NULL);
|
|
}
|
|
new_command = ft_lstnew(command);
|
|
if (new_command == NULL)
|
|
{
|
|
command_clear(command);
|
|
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
|
return (malloc_error(), NULL);
|
|
}
|
|
ft_lstadd_back(&commands, new_command);
|
|
if (current_token != NULL)
|
|
{
|
|
if (current_token->next == NULL)
|
|
syntax_error_unexpected_token((t_token *)current_token->content);
|
|
current_token = current_token->next;
|
|
}
|
|
}
|
|
return (commands);
|
|
}
|
|
|
|
/**
|
|
* @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);
|
|
}
|
|
|
|
char **args_to_array(
|
|
t_list *args,
|
|
size_t argc
|
|
)
|
|
{
|
|
char **argv;
|
|
size_t i;
|
|
|
|
argv = (char **)malloc(sizeof(char *) * (argc + 1));
|
|
if (argv == NULL)
|
|
return (NULL);
|
|
i = 0;
|
|
while (args != NULL)
|
|
{
|
|
argv[i] = (char *)args->content;
|
|
args = args->next;
|
|
i++;
|
|
}
|
|
argv[i] = NULL;
|
|
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)
|
|
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);
|
|
}
|
|
|
|
void print_command_info(
|
|
t_command *command
|
|
)
|
|
{
|
|
printf("Command:\n");
|
|
printf(" argc: %d\n", command->argc);
|
|
printf(" argv: [");
|
|
for (int i = 0; i < command->argc; i++)
|
|
{
|
|
printf("%s", command->argv[i]);
|
|
if (i < command->argc - 1)
|
|
printf(", ");
|
|
}
|
|
printf("]\n");
|
|
printf(" path: %s\n", command->path);
|
|
printf(" redirections:\n");
|
|
t_list *redirection_node = command->redirections;
|
|
while (redirection_node != NULL)
|
|
{
|
|
t_redirection *redirection = (t_redirection *)redirection_node->content;
|
|
printf(" type: %d, target: %s\n", redirection->type, redirection->target);
|
|
redirection_node = redirection_node->next;
|
|
}
|
|
printf(" heredocs:\n");
|
|
t_list *heredoc_node = command->heredocs;
|
|
while (heredoc_node != NULL)
|
|
{
|
|
t_redirection *heredoc = (t_redirection *)heredoc_node->content;
|
|
printf(" type: %d, target: %s\n", heredoc->type, heredoc->target);
|
|
heredoc_node = heredoc_node->next;
|
|
}
|
|
}
|
|
|
|
int main(int argc, char const *argv[])
|
|
{
|
|
t_list *commands;
|
|
char *line;
|
|
|
|
if (argc != 2)
|
|
return (EXIT_FAILURE);
|
|
line = ft_strdup(argv[1]);
|
|
commands = parse(line, NULL);
|
|
ft_lstiter(commands, (void (*)(void *))print_command_info);
|
|
if (line != NULL)
|
|
free(line);
|
|
if (commands != NULL)
|
|
ft_lstclear(&commands, (void (*)(void *))command_clear);
|
|
return 0;
|
|
}
|