diff options
| author | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
|---|---|---|
| committer | Martial Simon <msimon_fr@hotmail.com> | 2025-09-15 01:07:58 +0200 |
| commit | 967be9e750221ab2ab783f95df79bb26d290a45e (patch) | |
| tree | 6802900a5e975f9f68b169f0f503f040056d6952 /42sh/src/exec/ast_exec_functions.c | |
Diffstat (limited to '42sh/src/exec/ast_exec_functions.c')
| -rw-r--r-- | 42sh/src/exec/ast_exec_functions.c | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/42sh/src/exec/ast_exec_functions.c b/42sh/src/exec/ast_exec_functions.c new file mode 100644 index 0000000..9885997 --- /dev/null +++ b/42sh/src/exec/ast_exec_functions.c @@ -0,0 +1,491 @@ +#define _POSIX_C_SOURCE 200809L + +#include "exec/ast_exec_functions.h" + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "ast/ast_accessors.h" +#include "ast/ast_redirect.h" +#include "exec/ast_exec_redirs.h" +#include "lexer/expansion.h" +#include "utils/env.h" + +#define XCODE_TOENV(R) \ + char buf[16] = { 0 }; \ + sprintf(buf, "%d", (R)); \ + env_set("?", buf) + +static char **convert_args(struct string **args) +{ + size_t l = 0; + while (args[l]) + { + l++; + } + + char **c_args = calloc(l + 1, sizeof(char *)); + if (!c_args) + { + errx(EXIT_FAILURE, "convert_args: calloc failed."); + } + + for (size_t i = 0; i < l; i++) + { + c_args[i] = calloc(args[i]->length + 1, sizeof(char)); + if (!c_args[i]) + { + errx(EXIT_FAILURE, "convert_args: calloc failed."); + } + c_args[i] = strncpy(c_args[i], args[i]->data, args[i]->length); + } + + return c_args; +} + +static void free_args(char **args) +{ + size_t i = 0; + while (args[i]) + { + free(args[i]); + i++; + } + free(args); +} + +static builtin fetch_builtin(struct string *s) +{ + if (!s) + { + return NULL; + } + if (STRINGS_ARE_EQUAL(s->data, "false")) + { + return my_false; + } + if (STRINGS_ARE_EQUAL(s->data, "true")) + { + return my_true; + } + if (STRINGS_ARE_EQUAL(s->data, "echo")) + { + return echo; + } + if (STRINGS_ARE_EQUAL(s->data, ".")) + { + return dot; + } + if (STRINGS_ARE_EQUAL(s->data, "exit")) + { + return my_exit; + } + if (STRINGS_ARE_EQUAL(s->data, "cd")) + { + return cd; + } + if (STRINGS_ARE_EQUAL(s->data, "export")) + { + return export; + } + return NULL; +} + +static int exec_program(struct string **args) +{ + char **argv = convert_args(args); + if (!argv[0]) + { + free_args(argv); + return 127; + } + pid_t p = fork(); + if (!p) + { + execvp(argv[0], argv); + if (errno == ENOEXEC) + { + fprintf(stderr, + "exec_program: the file \'%s\' is not executable.\n", + argv[0]); + free_args(argv); + exit(126); + } + fprintf(stderr, "exec_program: command \'%s\' not found.\n", argv[0]); + free_args(argv); + exit(127); + } + else + { + int status = 0; + waitpid(p, &status, 0); + free_args(argv); + if (WIFEXITED(status)) + { + return WEXITSTATUS(status); + } + return SHELL_FALSE; + } +} + +static struct string **_copy_args(struct string **args) +{ + size_t len = 0; + while (args[len]) + { + len++; + } + struct string **cpy = calloc(len + 1, sizeof(struct string *)); + for (size_t i = 0; i < len; i++) + { + cpy[i] = string_deepcopy(args[i]); + } + return cpy; +} + +static void _free_str_args(struct string **args) +{ + size_t i = 0; + while (args[i]) + { + string_free(args[i]); + i++; + } + free(args); +} + +int exec_ast_command(struct ast *ast) +{ + union ast_caster ast_cast; + ast_cast.ast = ast; + struct string **cpy = _copy_args(ast_cast.ast_c->args); + if (!ast_cast.ast_c->args) + { + _free_str_args(cpy); + fprintf(stderr, "exec_ast_command: invalid args for a command exec.\n"); + XCODE_TOENV(SHELL_FALSE); + return SHELL_FALSE; + } + + size_t i = 0; + size_t j = 0; + while (ast_cast.ast_c->args[i]) + { + struct string *cv = expand_word(ast_cast.ast_c->args[i]); + if (cv->length > 0) + { + string_free(ast_cast.ast_c->args[j]); + ast_cast.ast_c->args[j++] = cv; + } + else + { + string_free(cv); + } + i++; + } + while (ast_cast.ast_c->args[j]) + { + string_free(ast_cast.ast_c->args[j]); + ast_cast.ast_c->args[j++] = NULL; + } + if (i == 0) + { + // No args ! + XCODE_TOENV(SHELL_TRUE); + return SHELL_TRUE; + } + builtin f = fetch_builtin(ast_cast.ast_c->args[0]); + int r = SHELL_TRUE; + if (f != NULL) + { + r = (*f)(ast_cast.ast_c->args + 1); + } + else + { + r = exec_program(ast_cast.ast_c->args); + } + _free_str_args(ast_cast.ast_c->args); + ast_cast.ast_c->args = cpy; + XCODE_TOENV(r); + return r; +} + +int exec_ast_list(struct ast *ast) +{ + union ast_caster ast_cast; + ast_cast.ast = ast; + int status = SHELL_TRUE; + for (size_t i = 0; i < ast_cast.ast_l->nb_children; i++) + { + status = eval_ast(get_i(ast_cast.ast, i)); + } + XCODE_TOENV(status); + return status; +} + +int exec_ast_if(struct ast *ast) +{ + int k = SHELL_TRUE; + if (eval_ast(get_i(ast, 0)) == SHELL_TRUE) + { + k = eval_ast(get_i(ast, 1)); + } + else + { + k = eval_ast(get_i(ast, 2)); + } + XCODE_TOENV(k); + return k; +} + +int exec_ast_logical(struct ast *ast) +{ + union ast_caster ast_cast; + ast_cast.ast = ast; + int k = SHELL_TRUE; + switch (ast_cast.ast_lo->type) + { + case TOKEN_NEG: + k = !eval_ast(get_left(ast)); + break; + case TOKEN_AND: + if (eval_ast(get_left(ast)) == SHELL_TRUE) + { + k = eval_ast(get_right(ast)); + break; + } + k = SHELL_FALSE; + break; + case TOKEN_OR: + if (eval_ast(get_left(ast)) == SHELL_FALSE) + { + k = eval_ast(get_right(ast)); + break; + } + k = SHELL_TRUE; + break; + default: + errx(SHELL_FALSE, + "exec_ast_logical: invalid token type for logical operation. Got: " + "%d", + ast_cast.ast_lo->type); + } + XCODE_TOENV(k); + return k; +} + +int exec_ast_pipe(struct ast *ast) +{ + union ast_caster ast_cast; + ast_cast.ast = ast; + + struct ast *left = ast_cast.ast_p->left; + struct ast *right = ast_cast.ast_p->right; + if (!left || !right) + { + XCODE_TOENV(EXIT_FAILURE); + return EXIT_FAILURE; + } + + int fds[2]; + if (pipe(fds) == -1) + { + XCODE_TOENV(EXIT_FAILURE); + return EXIT_FAILURE; + } + + pid_t p1 = fork(); + if (!p1) + { + close(fds[0]); + dup2(fds[1], STDOUT_FILENO); + int r1 = eval_ast(left); + exit(r1); + } + + pid_t p2 = fork(); + if (!p2) + { + close(fds[1]); + dup2(fds[0], STDIN_FILENO); + int r2 = eval_ast(right); + exit(r2); + } + + close(fds[0]); + close(fds[1]); + + int status = 0; + waitpid(p1, &status, 0); + waitpid(p2, &status, 0); + + if (WIFEXITED(status)) + { + int k = WEXITSTATUS(status); + XCODE_TOENV(k); + return k; + } + XCODE_TOENV(SHELL_FALSE); + return SHELL_FALSE; +} + +inline static bool _is_a_std_fd(int fd) +{ + return fd >= STDIN_FILENO && fd <= STDERR_FILENO; +} + +static int _exec_ast_redir(struct ast_redirection *ast, int left_fd, + int right_fd) +{ + // Save std fds + int cpy_stdin = dup(STDIN_FILENO); + int cpy_stdout = dup(STDOUT_FILENO); + int cpy_stderr = dup(STDERR_FILENO); + if (right_fd == CLOSE_FD) + { + close(left_fd); + } + else + { + dup2(right_fd, left_fd); + } + + int r = eval_ast(ast->expr); + + if (!_is_a_std_fd(right_fd)) + { + close(right_fd); + } + if (!_is_a_std_fd(left_fd)) + { + close(left_fd); + } + + // Restore std fds + dup2(cpy_stdin, STDIN_FILENO); + dup2(cpy_stdout, STDOUT_FILENO); + dup2(cpy_stderr, STDERR_FILENO); + XCODE_TOENV(r); + return r; +} + +int exec_ast_redirection(struct ast *ast) +{ + union ast_caster ast_cast; + ast_cast.ast = ast; + + int left_fd = 0; + int right_fd = 0; + find_fds(ast_cast.ast_r->redirect, &left_fd, &right_fd); + if (left_fd == BAD_FD || right_fd == BAD_FD) + { + fprintf(stderr, "redirection: bad fd.\n"); + XCODE_TOENV(SHELL_FALSE); + return SHELL_FALSE; + } + + int k = _exec_ast_redir(ast_cast.ast_r, left_fd, right_fd); + XCODE_TOENV(k); + return k; +} + +int exec_ast_while(struct ast *ast) +{ + int k = SHELL_TRUE; + while (true) + { + int r = eval_ast(((struct ast_while *)ast)->cond); + if (r != SHELL_TRUE) + { + break; + } + k = eval_ast(((struct ast_while *)ast)->body); + } + XCODE_TOENV(k); + return k; +} + +int exec_ast_assign(struct ast *ast) +{ + struct ast_assign *ast_a = (struct ast_assign *)ast; + struct string *expanded = expand_word(ast_a->val); + env_set(ast_a->name->data, expanded->data); + string_free(expanded); + XCODE_TOENV(SHELL_TRUE); + return SHELL_TRUE; +} + +int exec_ast_for(struct ast *ast) +{ + if (!get_left(ast)) + { + XCODE_TOENV(SHELL_TRUE); + return SHELL_TRUE; + } + struct string **args = ((struct ast_command *)get_left(ast))->args; + struct string *name = ((struct ast_for *)ast)->var; + int k = SHELL_TRUE; + size_t i = 0; + while (args[i]) + { + struct string *value = args[i]; + i++; + env_set(name->data, value->data); + k = eval_ast(get_right(ast)); + } + XCODE_TOENV(k); + return k; +} + +static int _exec_fork(struct ast *ast) +{ + pid_t child = fork(); + + if (child == -1) + { + XCODE_TOENV(SHELL_FALSE); + return SHELL_FALSE; + } + else if (child) + { + // Parent process + + int status; + // Why did I put 0 last time I used fork(2) + waitpid(child, &status, 0); + + if (!WIFEXITED(status)) + { + XCODE_TOENV(SHELL_FALSE); + return SHELL_FALSE; + } + + return WEXITSTATUS(status); + } + else + { + // Child process + + int ret = eval_ast(ast); + // Need to exit or else we will have to exit from the main function + // So we will execute the remainder of the ast twice + // Which is not what we want + exit(ret); + } +} + +int exec_ast_subshell(struct ast *ast) +{ + struct ast *sub = get_left(ast); + int fork_res = _exec_fork(sub); + // TODO:The exit codes might not be the correct one + XCODE_TOENV(fork_res); + return fork_res; +} |
