aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/io.h
blob: f8149a818398ea1a8d5159e49f12ede0ab6304fe (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#ifndef FISH_IO_H
#define FISH_IO_H

#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <vector>
// Note that we have to include something to get any _LIBCPP_VERSION defined so we can detect libc++
// So it's key that vector go above. If we didn't need vector for other reasons, we might include
// ciso646, which does nothing
#if defined(_LIBCPP_VERSION) || __cplusplus > 199711L
// C++11 or libc++ (which is a C++11-only library, but the memory header works OK in C++03)
#include <memory>
using std::shared_ptr;
#else
// C++03 or libstdc++
#include <tr1/memory>
using std::tr1::shared_ptr;
#endif

#include "common.h"

/// Describes what type of IO operation an io_data_t represents.
enum io_mode_t { IO_FILE, IO_PIPE, IO_FD, IO_BUFFER, IO_CLOSE };

/// Represents an FD redirection.
class io_data_t {
   private:
    // No assignment or copying allowed.
    io_data_t(const io_data_t &rhs);
    void operator=(const io_data_t &rhs);

   protected:
    io_data_t(io_mode_t m, int f) : io_mode(m), fd(f) {}

   public:
    /// Type of redirect.
    const io_mode_t io_mode;
    /// FD to redirect.
    const int fd;

    virtual void print() const = 0;
    virtual ~io_data_t() = 0;
};

class io_close_t : public io_data_t {
   public:
    explicit io_close_t(int f) : io_data_t(IO_CLOSE, f) {}

    virtual void print() const;
};

class io_fd_t : public io_data_t {
   public:
    /// fd to redirect specified fd to. For example, in 2>&1, old_fd is 1, and io_data_t::fd is 2.
    const int old_fd;

    /// Whether this redirection was supplied by a script. For example, 'cmd <&3' would have
    /// user_supplied set to true. But a redirection that comes about through transmogrification
    /// would not.
    const bool user_supplied;

    virtual void print() const;

    io_fd_t(int f, int old, bool us) : io_data_t(IO_FD, f), old_fd(old), user_supplied(us) {}
};

class io_file_t : public io_data_t {
   public:
    /// Filename, malloc'd. This needs to be used after fork, so don't use wcstring here.
    const char *const filename_cstr;
    /// file creation flags to send to open.
    const int flags;

    virtual void print() const;

    io_file_t(int f, const wcstring &fname, int fl = 0)
        : io_data_t(IO_FILE, f), filename_cstr(wcs2str(fname)), flags(fl) {}

    virtual ~io_file_t() { free((void *)filename_cstr); }
};

class io_pipe_t : public io_data_t {
   protected:
    io_pipe_t(io_mode_t m, int f, bool i) : io_data_t(m, f), is_input(i) {
        pipe_fd[0] = pipe_fd[1] = -1;
    }

   public:
    int pipe_fd[2];
    const bool is_input;

    virtual void print() const;

    io_pipe_t(int f, bool i) : io_data_t(IO_PIPE, f), is_input(i) { pipe_fd[0] = pipe_fd[1] = -1; }
};

class io_chain_t;
class io_buffer_t : public io_pipe_t {
   private:
    /// Buffer to save output in.
    std::vector<char> out_buffer;

    explicit io_buffer_t(int f) : io_pipe_t(IO_BUFFER, f, false /* not input */), out_buffer() {}

   public:
    virtual void print() const;

    virtual ~io_buffer_t();

    /// Function to append to the buffer.
    void out_buffer_append(const char *ptr, size_t count) {
        out_buffer.insert(out_buffer.end(), ptr, ptr + count);
    }

    /// Function to get a pointer to the buffer.
    char *out_buffer_ptr(void) { return out_buffer.empty() ? NULL : &out_buffer.at(0); }

    const char *out_buffer_ptr(void) const { return out_buffer.empty() ? NULL : &out_buffer.at(0); }

    /// Function to get the size of the buffer.
    size_t out_buffer_size(void) const { return out_buffer.size(); }

    /// Ensures that the pipes do not conflict with any fd redirections in the chain.
    bool avoid_conflicts_with_io_chain(const io_chain_t &ios);

    /// Close output pipe, and read from input pipe until eof.
    void read();

    /// Create a IO_BUFFER type io redirection, complete with a pipe and a vector<char> for output.
    /// The default file descriptor used is STDOUT_FILENO for buffering.
    ///
    /// \param fd the fd that will be mapped in the child process, typically STDOUT_FILENO
    /// \param conflicts A set of IO redirections. The function ensures that any pipe it makes does
    /// not conflict with an fd redirection in this list.
    static io_buffer_t *create(int fd, const io_chain_t &conflicts);
};

class io_chain_t : public std::vector<shared_ptr<io_data_t> > {
   public:
    io_chain_t();
    explicit io_chain_t(const shared_ptr<io_data_t> &);

    void remove(const shared_ptr<const io_data_t> &element);
    void push_back(const shared_ptr<io_data_t> &element);
    void push_front(const shared_ptr<io_data_t> &element);
    void append(const io_chain_t &chain);

    shared_ptr<const io_data_t> get_io_for_fd(int fd) const;
    shared_ptr<io_data_t> get_io_for_fd(int fd);
};

/// Return the last io redirection in the chain for the specified file descriptor.
shared_ptr<const io_data_t> io_chain_get(const io_chain_t &src, int fd);
shared_ptr<io_data_t> io_chain_get(io_chain_t &src, int fd);

/// Given a pair of fds, if an fd is used by the given io chain, duplicate that fd repeatedly until
/// we find one that does not conflict, or we run out of fds. Returns the new fds by reference,
/// closing the old ones. If we get an error, returns false (in which case both fds are closed and
/// set to -1).
bool pipe_avoid_conflicts_with_io_chain(int fds[2], const io_chain_t &ios);

/// Class representing the output that a builtin can generate.
class output_stream_t {
   private:
    // No copying.
    output_stream_t(const output_stream_t &s);
    void operator=(const output_stream_t &s);

    wcstring buffer_;

   public:
    output_stream_t() {}

    void append(const wcstring &s) { this->buffer_.append(s); }

    void append(const wchar_t *s) { this->buffer_.append(s); }

    void append(wchar_t s) { this->buffer_.push_back(s); }

    void append(const wchar_t *s, size_t amt) { this->buffer_.append(s, amt); }

    void push_back(wchar_t c) { this->buffer_.push_back(c); }

    void append_format(const wchar_t *format, ...) {
        va_list va;
        va_start(va, format);
        ::append_formatv(this->buffer_, format, va);
        va_end(va);
    }

    void append_formatv(const wchar_t *format, va_list va_orig) {
        ::append_formatv(this->buffer_, format, va_orig);
    }

    const wcstring &buffer() const { return this->buffer_; }

    bool empty() const { return buffer_.empty(); }
};

struct io_streams_t {
    output_stream_t out;
    output_stream_t err;

    // fd representing stdin. This is not closed by the destructor.
    int stdin_fd;

    // Whether stdin is "directly redirected," meaning it is the recipient of a pipe (foo | cmd) or
    // direct redirection (cmd < foo.txt). An "indirect redirection" would be e.g. begin ; cmd ; end
    // < foo.txt
    bool stdin_is_directly_redirected;

    // Indicates whether stdout and stderr are redirected (e.g. to a file or piped).
    bool out_is_redirected;
    bool err_is_redirected;

    // Actual IO redirections. This is only used by the source builtin. Unowned.
    const io_chain_t *io_chain;

    io_streams_t()
        : stdin_fd(-1),
          stdin_is_directly_redirected(false),
          out_is_redirected(false),
          err_is_redirected(false),
          io_chain(NULL) {}
};

#if 0
// Print debug information about the specified IO redirection chain to stderr.
void io_print(const io_chain_t &chain);
#endif

#endif