summaryrefslogtreecommitdiff
path: root/42sh/src/exec/ast_exec_functions.c
diff options
context:
space:
mode:
Diffstat (limited to '42sh/src/exec/ast_exec_functions.c')
-rw-r--r--42sh/src/exec/ast_exec_functions.c491
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;
+}