diff --git a/src/execution/execution.c b/src/execution/execution.c index 0066681..33389cb 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -5,9 +5,14 @@ #include #include #include +#include +#include +#include #include "../utils/ast/ast.h" +// --- Helpers --- + /** * @brief converts a linked list of command arguments to an argv array. Don't * forget to free the result @@ -44,6 +49,94 @@ static char **list_to_argv(struct list *command_list) return argv; } +// --- Builtins --- + +static int builtin_echo(char **argv) +{ + bool newline = true; + int i = 1; + + if (argv[1] && strcmp(argv[1], "-n") == 0) + { + newline = false; + i++; + } + + for (; argv[i]; i++) + { + printf("%s", argv[i]); + if (argv[i + 1]) + printf(" "); + } + if (newline) + printf("\n"); + + fflush(stdout); + return 0; +} + +static int builtin_true(char **argv) +{ + (void)argv; + return 0; +} + +static int builtin_false(char **argv) +{ + (void)argv; + return 1; +} + +static int builtin_exit(char **argv) +{ + int exit_val = 0; + if (argv[1]) + exit_val = atoi(argv[1]); + exit(exit_val); + return exit_val; +} + +static int builtin_cd(char **argv) +{ + const char *path = argv[1]; + if (!path) + { + path = getenv("HOME"); + if (!path) + { + fprintf(stderr, "cd: HOME not set\n"); + return 1; + } + } + if (chdir(path) != 0) + { + perror("cd"); + return 1; + } + return 0; +} + +static int try_builtin(char **argv) +{ + if (!argv || !argv[0]) + return 0; + + if (strcmp(argv[0], "echo") == 0) + return builtin_echo(argv); + if (strcmp(argv[0], "true") == 0) + return builtin_true(argv); + if (strcmp(argv[0], "false") == 0) + return builtin_false(argv); + if (strcmp(argv[0], "exit") == 0) + return builtin_exit(argv); + if (strcmp(argv[0], "cd") == 0) + return builtin_cd(argv); + + return -1; +} + +// --- Execution Core --- + /** * @brief Executes a command represented by an ast_command structure * @@ -54,7 +147,7 @@ static int exec_command(struct ast_command *command) { if (!command || !(command->command)) { - return -1; + return 1; } char **argv = list_to_argv(command->command); @@ -62,23 +155,30 @@ static int exec_command(struct ast_command *command) if (!argv || !(argv[0])) { free(argv); - return -1; + return 0; + } + + int builtin_ret = try_builtin(argv); + if (builtin_ret != -1) + { + free(argv); + return builtin_ret; } pid_t pid = fork(); - if (pid < 0) // Fork failed + if (pid < 0) { perror("fork"); free(argv); - return -1; + return 1; } - if (pid == 0) // If child process + if (pid == 0) { execvp(argv[0], argv); - perror("execvp"); // If execvp returns, there was an error - exit(EXIT_FAILURE); // Exit child process + perror("execvp"); + _exit(127); } int status = 0; @@ -90,7 +190,7 @@ static int exec_command(struct ast_command *command) return WEXITSTATUS(status); } - return -1; // Should not happen + return 1; } /** @@ -113,7 +213,7 @@ int execution(struct ast *ast) } case AST_CMD: { struct ast_command *command = ast_get_command(ast); - return exec_command(command); // It's recursive + return exec_command(command); } case AST_IF: { struct ast_if *if_node = ast_get_if(ast); @@ -127,8 +227,117 @@ int execution(struct ast *ast) return execution(if_node->else_clause); } } + case AST_LIST: { + struct ast_list *list_node = ast_get_list(ast); + struct list *cur = list_node->children; + int ret = 0; + while (cur) + { + struct ast *child = (struct ast *)cur->data; + ret = execution(child); + cur = cur->next; + } + return ret; + } + case AST_AND_OR: { + struct ast_and_or *ao_node = ast_get_and_or(ast); + int left_ret = execution(ao_node->left); + + if (ao_node->type == AST_AND_OR_TYPE_AND) + { + if (left_ret == 0) + return execution(ao_node->right); + return left_ret; + } + else // OR + { + if (left_ret != 0) + return execution(ao_node->right); + return left_ret; + } + } + case AST_REDIR: { + struct ast_redir *redir = ast_get_redir(ast); + + int fd_target = redir->io_number; + if (fd_target == -1) + { + if (redir->type == AST_REDIR_TYPE_LESS || redir->type == AST_REDIR_TYPE_DLESS || redir->type == AST_REDIR_TYPE_LESSAND) + fd_target = 0; + else + fd_target = 1; + } + + int saved_fd = dup(fd_target); + + int new_fd = -1; + int flags = 0; + int mode = 0644; + + if (redir->type == AST_REDIR_TYPE_GREAT || redir->type == AST_REDIR_TYPE_CLOBBER) + { + flags = O_WRONLY | O_CREAT | O_TRUNC; + new_fd = open(redir->filename, flags, mode); + } + else if (redir->type == AST_REDIR_TYPE_DGREAT) + { + flags = O_WRONLY | O_CREAT | O_APPEND; + new_fd = open(redir->filename, flags, mode); + } + else if (redir->type == AST_REDIR_TYPE_LESS) + { + flags = O_RDONLY; + new_fd = open(redir->filename, flags); + } + else if (redir->type == AST_REDIR_TYPE_GREATAND || redir->type == AST_REDIR_TYPE_LESSAND) + { + // Simple fd duplication + new_fd = atoi(redir->filename); + // Verify new_fd is valid? dup2 will check. + if (dup2(new_fd, fd_target) == -1) + { + perror("dup2"); + if (saved_fd != -1) close(saved_fd); + return 1; + } + new_fd = -2; // Mark as "already duped" + } + + if (new_fd == -1) + { + perror("open"); + if (saved_fd != -1) close(saved_fd); + return 1; + } + + if (new_fd != -2) + { + if (dup2(new_fd, fd_target) == -1) + { + perror("dup2"); + close(new_fd); + if (saved_fd != -1) close(saved_fd); + return 1; + } + close(new_fd); + } + + int ret = execution(redir->child); + + if (saved_fd != -1) + { + dup2(saved_fd, fd_target); + close(saved_fd); + } + else + { + close(fd_target); + } + + return ret; + } default: { - return -1; // Should not happen + return 127; } } -} +} \ No newline at end of file diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index d1aa675..6cd107d 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -6,6 +6,8 @@ libutils_a_SOURCES = \ ast/ast_if.c \ ast/ast_command.c \ ast/ast_list.c \ + ast/ast_and_or.c \ + ast/ast_redir.c \ ast/ast_void.c \ ast/ast_end.c \ lists/lists.c \ diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index 2d0ce84..c4bece9 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -13,6 +13,10 @@ void ast_free(struct ast **node) ast_free_command(ast_get_command(*node)); else if (ast_is_list(*node)) ast_free_list(ast_get_list(*node)); + else if (ast_is_and_or(*node)) + ast_free_and_or(ast_get_and_or(*node)); + else if (ast_is_redir(*node)) + ast_free_redir(ast_get_redir(*node)); free(*node); *node = NULL; diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h index 3984667..1062782 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -4,6 +4,8 @@ #include "utils/ast/ast_base.h" #include "utils/ast/ast_command.h" #include "utils/ast/ast_if.h" +#include "utils/ast/ast_and_or.h" +#include "utils/ast/ast_redir.h" #include "utils/ast/ast_list.h" #include "utils/ast/ast_end.h" #include "utils/ast/ast_void.h" diff --git a/src/utils/ast/ast_and_or.c b/src/utils/ast/ast_and_or.c new file mode 100644 index 0000000..d07eaf5 --- /dev/null +++ b/src/utils/ast/ast_and_or.c @@ -0,0 +1,35 @@ +#include "utils/ast/ast_and_or.h" +#include + +bool ast_is_and_or(struct ast *node) +{ + return node != NULL && node->type == AST_AND_OR; +} + +struct ast_and_or *ast_get_and_or(struct ast *node) +{ + if (ast_is_and_or(node)) + return (struct ast_and_or *)node->data; + return NULL; +} + +struct ast *ast_create_and_or(struct ast *left, struct ast *right, enum ast_and_or_type type) +{ + struct ast_and_or *and_or = malloc(sizeof(struct ast_and_or)); + if (!and_or) + return NULL; + and_or->left = left; + and_or->right = right; + and_or->type = type; + + return ast_create(AST_AND_OR, and_or); +} + +void ast_free_and_or(struct ast_and_or *and_or) +{ + if (!and_or) + return; + ast_free(&and_or->left); + ast_free(&and_or->right); + free(and_or); +} diff --git a/src/utils/ast/ast_and_or.h b/src/utils/ast/ast_and_or.h new file mode 100644 index 0000000..57a9668 --- /dev/null +++ b/src/utils/ast/ast_and_or.h @@ -0,0 +1,23 @@ +#ifndef AST_AND_OR_H +#define AST_AND_OR_H + +#include +#include "utils/ast/ast_base.h" + +enum ast_and_or_type { + AST_AND_OR_TYPE_AND, + AST_AND_OR_TYPE_OR +}; + +struct ast_and_or { + struct ast *left; + struct ast *right; + enum ast_and_or_type type; +}; + +bool ast_is_and_or(struct ast *node); +struct ast_and_or *ast_get_and_or(struct ast *node); +struct ast *ast_create_and_or(struct ast *left, struct ast *right, enum ast_and_or_type type); +void ast_free_and_or(struct ast_and_or *and_or); + +#endif /* ! AST_AND_OR_H */ diff --git a/src/utils/ast/ast_base.h b/src/utils/ast/ast_base.h index 432eb2c..e09b25d 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -8,6 +8,8 @@ enum ast_type AST_END, AST_LIST, AST_IF, + AST_AND_OR, + AST_REDIR, AST_VOID, AST_CMD }; @@ -20,7 +22,9 @@ struct ast * Data associated with this AST node. It can be one of the following: * - NULL (AST_END) * - struct ast_if* (AST_IF) - * - struct ast_cmd* (AST_CMD) + * - struct ast_command* (AST_CMD) + * - struct ast_and_or* (AST_AND_OR) + * - struct ast_redir* (AST_REDIR) */ void *data; }; diff --git a/src/utils/ast/ast_list.c b/src/utils/ast/ast_list.c index 95e7047..4fc87f5 100644 --- a/src/utils/ast/ast_list.c +++ b/src/utils/ast/ast_list.c @@ -29,7 +29,7 @@ void ast_free_list(struct ast_list *ast_list) if (ast_list == NULL) return; - list_deep_destroy(ast_list->children); + ast_list_deep_destroy(ast_list->children); free(ast_list); } @@ -41,7 +41,8 @@ void ast_list_deep_destroy(struct list *l) { next_elt = elt->next; - ast_free(elt->data); + struct ast *node = (struct ast *)elt->data; + ast_free(&node); free(elt); elt = next_elt; } diff --git a/src/utils/ast/ast_redir.c b/src/utils/ast/ast_redir.c new file mode 100644 index 0000000..c5eec63 --- /dev/null +++ b/src/utils/ast/ast_redir.c @@ -0,0 +1,36 @@ +#include "utils/ast/ast_redir.h" +#include + +bool ast_is_redir(struct ast *node) +{ + return node != NULL && node->type == AST_REDIR; +} + +struct ast_redir *ast_get_redir(struct ast *node) +{ + if (ast_is_redir(node)) + return (struct ast_redir *)node->data; + return NULL; +} + +struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, enum ast_redir_type type) +{ + struct ast_redir *redir = malloc(sizeof(struct ast_redir)); + if (!redir) + return NULL; + redir->child = child; + redir->filename = filename; // Takes ownership? Usually yes in simple ASTs, or dup. Let's assume pointer copy for now, but user must manage memory. + redir->io_number = io_number; + redir->type = type; + + return ast_create(AST_REDIR, redir); +} + +void ast_free_redir(struct ast_redir *redir) +{ + if (!redir) + return; + ast_free(&redir->child); + free(redir->filename); + free(redir); +} diff --git a/src/utils/ast/ast_redir.h b/src/utils/ast/ast_redir.h new file mode 100644 index 0000000..5f5ffb5 --- /dev/null +++ b/src/utils/ast/ast_redir.h @@ -0,0 +1,29 @@ +#ifndef AST_REDIR_H +#define AST_REDIR_H + +#include +#include "utils/ast/ast_base.h" + +enum ast_redir_type { + AST_REDIR_TYPE_LESS, // < + AST_REDIR_TYPE_GREAT, // > + AST_REDIR_TYPE_DLESS, // << + AST_REDIR_TYPE_DGREAT, // >> + AST_REDIR_TYPE_LESSAND, // <& + AST_REDIR_TYPE_GREATAND, // >& + AST_REDIR_TYPE_CLOBBER // >| +}; + +struct ast_redir { + struct ast *child; + char *filename; + int io_number; // The FD being redirected (default -1 if not specified, implies 0 or 1 based on type) + enum ast_redir_type type; +}; + +bool ast_is_redir(struct ast *node); +struct ast_redir *ast_get_redir(struct ast *node); +struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, enum ast_redir_type type); +void ast_free_redir(struct ast_redir *redir); + +#endif /* ! AST_REDIR_H */