feat: exit builtin

This commit is contained in:
Gu://em_ 2026-01-31 18:27:48 +01:00
parent 17f8b918c8
commit 3b62c56756
3 changed files with 81 additions and 25 deletions

View file

@ -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;
}
}

View file

@ -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);
@ -144,6 +170,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);
@ -205,20 +233,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;
}
}
@ -234,7 +259,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;
}
@ -246,16 +272,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;
@ -265,9 +289,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;
@ -454,13 +475,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)
@ -509,7 +538,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);

View file

@ -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);