summaryrefslogtreecommitdiff
path: root/42sh/src/exec/ast_exec_redirs.c
blob: 5cd013c8f01108fd9e5f62ceb560eeeb2c4e7812 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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);
    }
}