diff --git a/src/execution/execution.c b/src/execution/execution.c index 46bbfcb..fff9be2 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -9,6 +9,7 @@ #include "../expansion/expansion.h" #include "../utils/hash_map/hash_map.h" +#include "../utils/vars/vars.h" // Refactored: delegates to helpers in execution_helpers.c #include "execution_helpers.h" @@ -18,27 +19,52 @@ int execution(struct ast *ast, struct hash_map *vars) if (!ast) return 0; + int res; switch (ast->type) { case AST_VOID: case AST_END: - return 0; + res = 0; + break; case AST_CMD: { struct ast_command *command = ast_get_command(ast); if (!expand(command, vars)) fprintf(stderr, "Error: Variable expansion failed\n"); - return exec_ast_command(command, vars); + res = exec_ast_command(command, vars); + break; } case AST_IF: - return exec_ast_if(ast_get_if(ast), vars); + res = exec_ast_if(ast_get_if(ast), vars); + break; case AST_LIST: - return exec_ast_list(ast_get_list(ast), vars); + res = exec_ast_list(ast_get_list(ast), vars); + break; case AST_AND_OR: - return exec_ast_and_or(ast_get_and_or(ast), vars); + res = exec_ast_and_or(ast_get_and_or(ast), vars); + break; case AST_LOOP: - return exec_ast_loop(ast_get_loop(ast), vars); + res = exec_ast_loop(ast_get_loop(ast), vars); + break; default: - return 127; + res = 127; + break; + } + + if (res == EXEC_SIGNAL_EXIT) + { + char *exit_val_str = get_var(vars, "EXIT_VALUE"); + if (exit_val_str == NULL) + { + fprintf( + stderr, + "Internal error: could not retrieve return value from exit\n"); + return 2; + } + return atoi(exit_val_str); + } + else + { + return res; } } diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c index 4d1848a..59580e8 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -14,6 +14,8 @@ #include "../utils/vars/vars.h" #include "execution.h" +// === Static functions + static int open_redir_file(const struct ast_redir *redir, int *flags, int *mode) { *mode = 0644; @@ -121,6 +123,30 @@ static char **list_to_argv(struct list *command_list) return argv; } +/* + * @brief parses string and returns the represented (unsigned) number + * @return the number contained by the string or -1 if number is invalid + */ +static int atou(char *str) +{ + if (str == NULL || *str == '\0') + return -1; + + int result = 0; + size_t i = 0; + while (str[i] != '\0') + { + if (str[i] < '0' || str[i] > '9') + return -1; + + result *= 10; + result += str[i] - '0'; + i++; + } + + return result; +} + static int try_builtin(char **argv, struct hash_map *vars); static int builtin_break(char **argv); @@ -145,6 +171,8 @@ static int exec_assignment(struct list *assignment_list, struct hash_map *vars) return 0; } +// === Functions + int exec_ast_command(struct ast_command *command, struct hash_map *vars) { exec_assignment(command->assignments, vars); @@ -206,20 +234,17 @@ int exec_ast_if(struct ast_if *if_node, struct hash_map *vars) if (if_node == NULL) return 2; int cond = execution(if_node->condition, vars); - if (cond == EXEC_SIGNAL_BREAK || cond == EXEC_SIGNAL_CONTINUE) + if (cond == EXEC_SIGNAL_BREAK || cond == EXEC_SIGNAL_CONTINUE + || cond == EXEC_SIGNAL_EXIT) return cond; if (cond == 0) { int r = execution(if_node->then_clause, vars); - if (r == EXEC_SIGNAL_BREAK || r == EXEC_SIGNAL_CONTINUE) - return r; return r; } else { int r = execution(if_node->else_clause, vars); - if (r == EXEC_SIGNAL_BREAK || r == EXEC_SIGNAL_CONTINUE) - return r; return r; } } @@ -235,7 +260,8 @@ int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) { int child_ret = execution(child, vars); if (child_ret == EXEC_SIGNAL_BREAK - || child_ret == EXEC_SIGNAL_CONTINUE) + || child_ret == EXEC_SIGNAL_CONTINUE + || child_ret == EXEC_SIGNAL_EXIT) return child_ret; ret = child_ret; } @@ -247,16 +273,14 @@ int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars) { int left_ret = execution(ao_node->left, vars); - if (left_ret == EXEC_SIGNAL_BREAK || left_ret == EXEC_SIGNAL_CONTINUE) + if (left_ret == EXEC_SIGNAL_BREAK || left_ret == EXEC_SIGNAL_CONTINUE + || left_ret == EXEC_SIGNAL_EXIT) return left_ret; if (ao_node->type == AST_AND_OR_TYPE_AND) { if (left_ret == 0) { int right_ret = execution(ao_node->right, vars); - if (right_ret == EXEC_SIGNAL_BREAK - || right_ret == EXEC_SIGNAL_CONTINUE) - return right_ret; return right_ret; } return left_ret; @@ -266,9 +290,6 @@ int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars) if (left_ret != 0) { int right_ret = execution(ao_node->right, vars); - if (right_ret == EXEC_SIGNAL_BREAK - || right_ret == EXEC_SIGNAL_CONTINUE) - return right_ret; return right_ret; } return left_ret; @@ -455,13 +476,21 @@ static int builtin_false(char **argv) return 1; } -static int builtin_exit(char **argv) +static int builtin_exit(char **argv, struct hash_map *vars) { int exit_val = 0; if (argv[1]) - exit_val = atoi(argv[1]); - exit(exit_val); - return exit_val; + { + exit_val = atou(argv[1]); + if (exit_val == -1) + { + fprintf(stderr, "exit: Illegal number %s\n", argv[1]); + return 2; + } + } + + set_var_int(vars, "EXIT_VALUE", exit_val); + return EXEC_SIGNAL_EXIT; } static int builtin_cd(char **argv, struct hash_map *vars) @@ -529,7 +558,7 @@ static int try_builtin(char **argv, struct hash_map *vars) if (strcmp(argv[0], "continue") == 0) return builtin_continue(argv); if (strcmp(argv[0], "exit") == 0) - return builtin_exit(argv); + return builtin_exit(argv, vars); if (strcmp(argv[0], "cd") == 0) return builtin_cd(argv, vars); diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h index 530b4dc..ee28c3d 100644 --- a/src/execution/execution_helpers.h +++ b/src/execution/execution_helpers.h @@ -7,6 +7,7 @@ // Special execution signals used internally to implement loop control #define EXEC_SIGNAL_CONTINUE (-2) #define EXEC_SIGNAL_BREAK (-3) +#define EXEC_SIGNAL_EXIT (-4) int exec_ast_command(struct ast_command *command, struct hash_map *vars); int exec_ast_if(struct ast_if *if_node, struct hash_map *vars);