summaryrefslogtreecommitdiff
path: root/42sh/src/exec/ast_exec_redirs.c
diff options
context:
space:
mode:
Diffstat (limited to '42sh/src/exec/ast_exec_redirs.c')
-rw-r--r--42sh/src/exec/ast_exec_redirs.c149
1 files changed, 149 insertions, 0 deletions
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);
+ }
+}