summaryrefslogtreecommitdiff
path: root/42sh/src/exec
diff options
context:
space:
mode:
Diffstat (limited to '42sh/src/exec')
-rw-r--r--42sh/src/exec/Makefile.am15
-rw-r--r--42sh/src/exec/ast_eval.c20
-rw-r--r--42sh/src/exec/ast_eval.h24
-rw-r--r--42sh/src/exec/ast_exec_functions.c491
-rw-r--r--42sh/src/exec/ast_exec_functions.h77
-rw-r--r--42sh/src/exec/ast_exec_redirs.c149
-rw-r--r--42sh/src/exec/ast_exec_redirs.h11
7 files changed, 787 insertions, 0 deletions
diff --git a/42sh/src/exec/Makefile.am b/42sh/src/exec/Makefile.am
new file mode 100644
index 0000000..a6a9134
--- /dev/null
+++ b/42sh/src/exec/Makefile.am
@@ -0,0 +1,15 @@
+lib_LIBRARIES = libeval.a
+
+libeval_a_SOURCES = \
+ ast_exec_functions.c \
+ ast_exec_functions.h \
+ ast_eval.h \
+ ast_eval.c \
+ ast_exec_redirs.h \
+ ast_exec_redirs.c
+
+libeval_a_CPPFLAGS = -I$(top_srcdir)/src
+
+libeval_a_CFLAGS = -std=c99 -Werror -Wall -Wextra -Wvla -pedantic
+
+noinst_LIBRARIES = libeval.a
diff --git a/42sh/src/exec/ast_eval.c b/42sh/src/exec/ast_eval.c
new file mode 100644
index 0000000..db152f8
--- /dev/null
+++ b/42sh/src/exec/ast_eval.c
@@ -0,0 +1,20 @@
+#include "exec/ast_eval.h"
+
+#include "exec/ast_exec_functions.h"
+
+static const exec_f exec_node_ltable[] = {
+ [AST_LIST] = exec_ast_list, [AST_IF] = exec_ast_if,
+ [AST_COMMAND] = exec_ast_command, [AST_LOGICAL] = exec_ast_logical,
+ [AST_PIPELINE] = exec_ast_pipe, [AST_REDIRECTION] = exec_ast_redirection,
+ [AST_WHILE] = exec_ast_while, [AST_ASSIGN] = exec_ast_assign,
+ [AST_FOR] = exec_ast_for, [AST_SUBSHELL] = exec_ast_subshell
+};
+
+int eval_ast(struct ast *ast)
+{
+ if (!ast)
+ {
+ return SHELL_TRUE;
+ }
+ return exec_node_ltable[ast->type](ast);
+}
diff --git a/42sh/src/exec/ast_eval.h b/42sh/src/exec/ast_eval.h
new file mode 100644
index 0000000..785df5a
--- /dev/null
+++ b/42sh/src/exec/ast_eval.h
@@ -0,0 +1,24 @@
+#ifndef AST_EVAL_H
+#define AST_EVAL_H
+
+#include "ast/ast.h"
+
+/**
+ * @brief Shell true value, this is not the builtin true.
+ */
+#define SHELL_TRUE 0
+
+/**
+ * @brief Shell false value, this is not the builtin false.
+ */
+#define SHELL_FALSE 1
+
+/**
+ * @brief Returns the exit code of the given AST.
+ * @param ast The AST to evaluate. It is given as a `struct ast*`
+ * and uses an inheritance-like principle.
+ * An AST can be of any type from the `enum ast_node`.
+ */
+int eval_ast(struct ast *ast);
+
+#endif /* ! AST_EVAL_H */
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;
+}
diff --git a/42sh/src/exec/ast_exec_functions.h b/42sh/src/exec/ast_exec_functions.h
new file mode 100644
index 0000000..ba2fbcd
--- /dev/null
+++ b/42sh/src/exec/ast_exec_functions.h
@@ -0,0 +1,77 @@
+#ifndef AST_EXEC_FUNCTIONS_H
+#define AST_EXEC_FUNCTIONS_H
+
+#include "builtins/builtins.h"
+#include "exec/ast_eval.h"
+
+#define BASE_RW_SIZE 256
+
+typedef int (*exec_f)(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of it.
+ * A command can be a shell builtin or an external binary file called with
+ * `execvp`.
+ * @param ast An `AST_COMMAND` AST to evaluate. It is given as a `struct ast*`
+ * and uses inheritance-like principle.
+ */
+int exec_ast_command(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of it.
+ * It performs a lazy evaluation.
+ * @param ast An `AST_LOGICAL` AST to evaluate. It is given as a `struct ast*`
+ * and uses inheritance-like principle.
+ */
+int exec_ast_logical(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of the last
+ * `AST_COMMAND`.
+ * @param ast An `AST_LIST` AST to evaluate. It is given as a `struct ast*`
+ * and uses inheritance-like principle.
+ */
+int exec_ast_list(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of it.
+ * It performs a lazy evaluation.
+ * @param ast An `AST_IF` AST to evaluate. It is given as a `struct ast*`
+ * and uses inheritance-like principle.
+ */
+int exec_ast_if(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of it.
+ * It redirects the standard output of `ast->left` to the standard input of
+ * `ast->right`.
+ * @param ast An `AST_PIPELINE` AST to evaluate. It is given as a `struct ast*`
+ * and uses inheritance-like principle.
+ */
+int exec_ast_pipe(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of it.
+ * It gets the redirection details from `ast->redir` which is a `struct
+ * redirect`.
+ * @param ast An `AST_REDIRECTION` AST to evaluate. It is given as a `struct
+ * ast*` and uses inheritance-like principle.
+ */
+int exec_ast_redirection(struct ast *ast);
+
+int exec_ast_while(struct ast *ast);
+
+int exec_ast_assign(struct ast *ast);
+
+int exec_ast_for(struct ast *ast);
+
+/**
+ * @brief Evaluates the given AST and return the exit code of it.
+ * It gets the ast corresponding to the subshell and executes it in a new
+ * environment thanks to the fork(2) function.
+ * @param ast An `AST_SUBSHELL` AST to evaluate It is given as a `struct ast*`
+ * and uses inheritance-like principle.
+ */
+int exec_ast_subshell(struct ast *ast);
+
+#endif /* ! AST_EXEC_FUNCTIONS_H */
diff --git a/42sh/src/exec/ast_exec_redirs.c b/42sh/src/exec/ast_exec_redirs.c
new file mode 100644
index 0000000..5cd013c
--- /dev/null
+++ b/42sh/src/exec/ast_exec_redirs.c
@@ -0,0 +1,149 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include "exec/ast_exec_redirs.h"
+
+#include <err.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.h"
+#include "ast/ast_accessors.h"
+#include "ast/ast_redirect.h"
+#include "utils/env.h"
+#include "utils/libstring.h"
+
+inline static long _get_open_max(void)
+{
+ return sysconf(_SC_OPEN_MAX);
+}
+
+static bool _file_exists(char *filename)
+{
+ struct stat s;
+ return stat(filename, &s) == 0;
+}
+
+inline static bool _is_a_digit(char c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static bool _is_a_number(struct string *s)
+{
+ size_t i = 0;
+ while (i < s->length && _is_a_digit(s->data[i]))
+ {
+ i++;
+ }
+ return i == s->length;
+}
+
+/**
+ * @brief Assigns the fd described by s to out_fd if s
+ * describes an integer which is less than _SC_OPEN_MAX.
+ */
+static void _assign_if_valid_int_fd(struct string *s, int *out_fd)
+{
+ if (_is_a_number(s))
+ {
+ int fd = atoi(s->data);
+ if (fd < _get_open_max())
+ {
+ *out_fd = fd;
+ return;
+ }
+ }
+ *out_fd = BAD_FD;
+}
+
+/**
+ * @brief Assigns the fd described by s to out_fd if s
+ * describes an integer which is less than _SC_OPEN_MAX.
+ * s can also be exactly '-', in which case out_fd contains
+ * the value CLOSE_FD.
+ */
+static void _assign_if_valid_fd_and(struct string *s, int *out_fd)
+{
+ if (_is_a_number(s))
+ {
+ int fd = atoi(s->data);
+ int cpy_fd = dup(fd);
+ *out_fd = cpy_fd;
+ return;
+ }
+ if (s->length == 1 && s->data[0] == '-')
+ {
+ *out_fd = CLOSE_FD;
+ return;
+ }
+ *out_fd = BAD_FD;
+}
+
+void find_fds(struct redirect r, int *left_fd, int *right_fd)
+{
+ char *rd = r.redir->data;
+ static const int filemode = 0644;
+
+ // Default value is no valid left fd is passed in `r`.
+ *left_fd = BAD_FD;
+ *right_fd = BAD_FD;
+
+ if (STRINGS_ARE_EQUAL(rd, ">"))
+ {
+ // Sec. 2.7.2: noclobber must avoid overwritting on an existing file
+ if (env_get("noclobber") != NULL && _file_exists(r.file->data))
+ {
+ fprintf(stderr, "redirection: Unable to overwrite on %s.\n",
+ r.file->data);
+ return;
+ }
+ *right_fd = open(r.file->data, O_WRONLY | O_CREAT | O_TRUNC, filemode);
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ }
+ if (STRINGS_ARE_EQUAL(rd, ">>"))
+ {
+ *right_fd = open(r.file->data, O_WRONLY | O_CREAT | O_APPEND, filemode);
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ }
+ if (STRINGS_ARE_EQUAL(rd, ">|"))
+ {
+ // Sec. 2.7.2: this redir bypasses noclobber
+ *right_fd = open(r.file->data, O_WRONLY | O_CREAT | O_TRUNC, filemode);
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ }
+ if (STRINGS_ARE_EQUAL(rd, ">&"))
+ {
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ _assign_if_valid_fd_and(r.file, right_fd);
+ if (*right_fd == BAD_FD)
+ {
+ // Sure why not after all,
+ // Because when it is about bash posix
+ // 'undefined' in the SCL means:
+ // OH <&{filename} is AMBIGUOUS
+ // BUT >&{filename} is PERFECTLY FINE
+ *right_fd =
+ open(r.file->data, O_WRONLY | O_CREAT | O_TRUNC, filemode);
+ }
+ }
+ if (STRINGS_ARE_EQUAL(rd, "<"))
+ {
+ *right_fd = open(r.file->data, O_RDONLY);
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ }
+ if (STRINGS_ARE_EQUAL(rd, "<&"))
+ {
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ _assign_if_valid_fd_and(r.file, right_fd);
+ }
+ if (STRINGS_ARE_EQUAL(rd, "<>"))
+ {
+ *right_fd = open(r.file->data, O_RDWR | O_CREAT, filemode);
+ _assign_if_valid_int_fd(r.fd, left_fd);
+ }
+}
diff --git a/42sh/src/exec/ast_exec_redirs.h b/42sh/src/exec/ast_exec_redirs.h
new file mode 100644
index 0000000..4fd32de
--- /dev/null
+++ b/42sh/src/exec/ast_exec_redirs.h
@@ -0,0 +1,11 @@
+#ifndef AST_EXEC_REDIRS_H
+#define AST_EXEC_REDIRS_H
+
+#include "ast/ast_redirect.h"
+
+#define BAD_FD -1
+#define CLOSE_FD -2
+
+void find_fds(struct redirect r, int *left_fd, int *right_fd);
+
+#endif /* ! AST_EXEC_REDIRS_H */