2005-10-08 21:20:51 +10:00
|
|
|
#ifndef FISH_IO_H
|
|
|
|
#define FISH_IO_H
|
|
|
|
|
2016-05-01 20:32:40 -07:00
|
|
|
#include <stdarg.h>
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <unistd.h>
|
2017-02-13 20:37:27 -08:00
|
|
|
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <cstdint>
|
|
|
|
#include <cwchar>
|
2019-11-23 15:52:53 -08:00
|
|
|
#include <future>
|
2019-02-02 17:53:40 -08:00
|
|
|
#include <memory>
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
2019-02-01 01:58:06 -08:00
|
|
|
#include <vector>
|
2012-03-04 02:35:30 -08:00
|
|
|
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "common.h"
|
2023-10-08 23:22:27 +02:00
|
|
|
#include "cxx.h"
|
2021-02-02 16:44:33 -08:00
|
|
|
#include "fds.h"
|
2019-11-23 14:11:07 -08:00
|
|
|
#include "global_safety.h"
|
2019-12-12 16:44:24 -08:00
|
|
|
#include "redirection.h"
|
2023-01-14 14:56:24 -08:00
|
|
|
#include "signals.h"
|
2023-10-08 23:22:27 +02:00
|
|
|
#if INCLUDE_RUST_HEADERS
|
|
|
|
#include "io.rs.h"
|
|
|
|
#else
|
|
|
|
struct IoChain;
|
|
|
|
struct IoStreams;
|
|
|
|
struct OutputStreamFfi;
|
|
|
|
#endif
|
|
|
|
using output_stream_t = OutputStreamFfi;
|
|
|
|
using io_streams_t = IoStreams;
|
|
|
|
|
|
|
|
// null_output_stream_t
|
2019-02-01 01:58:06 -08:00
|
|
|
|
|
|
|
using std::shared_ptr;
|
2015-07-25 23:14:25 +08:00
|
|
|
|
2023-02-25 16:42:45 -06:00
|
|
|
struct job_group_t;
|
2020-02-08 14:34:10 -08:00
|
|
|
|
2021-02-04 16:23:58 -08:00
|
|
|
/// separated_buffer_t represents a buffer of output from commands, prepared to be turned into a
|
|
|
|
/// variable. For example, command substitutions output into one of these. Most commands just
|
|
|
|
/// produce a stream of bytes, and those get stored directly. However other commands produce
|
|
|
|
/// explicitly separated output, in particular `string` like `string collect` and `string split0`.
|
|
|
|
/// The buffer tracks a sequence of elements. Some elements are explicitly separated and should not
|
|
|
|
/// be further split; other elements have inferred separation and may be split by IFS (or not,
|
|
|
|
/// depending on its value).
|
2018-05-29 21:11:34 -07:00
|
|
|
enum class separation_type_t {
|
2021-02-04 16:23:58 -08:00
|
|
|
inferred, // this element should be further separated by IFS
|
|
|
|
explicitly, // this element is explicitly separated and should not be further split
|
2018-05-29 21:11:34 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/// A separated_buffer_t contains a list of elements, some of which may be separated explicitly and
|
|
|
|
/// others which must be separated further by the user (e.g. via IFS).
|
2021-07-22 10:43:25 -07:00
|
|
|
class separated_buffer_t : noncopyable_t {
|
2018-05-29 21:11:50 -07:00
|
|
|
public:
|
2018-05-29 21:11:34 -07:00
|
|
|
struct element_t {
|
2021-02-04 15:32:11 -08:00
|
|
|
std::string contents;
|
2018-05-29 21:11:34 -07:00
|
|
|
separation_type_t separation;
|
|
|
|
|
2021-02-04 15:32:11 -08:00
|
|
|
element_t(std::string contents, separation_type_t sep)
|
2018-05-29 21:11:34 -07:00
|
|
|
: contents(std::move(contents)), separation(sep) {}
|
|
|
|
|
|
|
|
bool is_explicitly_separated() const { return separation == separation_type_t::explicitly; }
|
|
|
|
};
|
|
|
|
|
2021-07-22 10:43:25 -07:00
|
|
|
/// We not be copied but may be moved.
|
2021-02-04 17:13:15 -08:00
|
|
|
/// Note this leaves the moved-from value in a bogus state until clear() is called on it.
|
2021-05-10 15:58:05 -07:00
|
|
|
separated_buffer_t(separated_buffer_t &&) = default;
|
2021-02-04 17:02:31 -08:00
|
|
|
separated_buffer_t &operator=(separated_buffer_t &&) = default;
|
|
|
|
|
2019-05-05 12:09:25 +02:00
|
|
|
/// Construct a separated_buffer_t with the given buffer limit \p limit, or 0 for no limit.
|
|
|
|
separated_buffer_t(size_t limit) : buffer_limit_(limit) {}
|
|
|
|
|
|
|
|
/// \return the buffer limit size, or 0 for no limit.
|
|
|
|
size_t limit() const { return buffer_limit_; }
|
|
|
|
|
|
|
|
/// \return the contents size.
|
|
|
|
size_t size() const { return contents_size_; }
|
|
|
|
|
|
|
|
/// \return whether the output has been discarded.
|
2021-02-04 16:06:28 -08:00
|
|
|
bool discarded() const { return discard_; }
|
2019-05-05 12:09:25 +02:00
|
|
|
|
|
|
|
/// Serialize the contents to a single string, where explicitly separated elements have a
|
|
|
|
/// newline appended.
|
2021-02-04 15:32:11 -08:00
|
|
|
std::string newline_serialized() const {
|
|
|
|
std::string result;
|
2019-05-05 12:09:25 +02:00
|
|
|
result.reserve(size());
|
|
|
|
for (const auto &elem : elements_) {
|
|
|
|
result.append(elem.contents);
|
|
|
|
if (elem.is_explicitly_separated()) {
|
|
|
|
result.push_back('\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
2018-05-29 21:11:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// \return the list of elements.
|
|
|
|
const std::vector<element_t> &elements() const { return elements_; }
|
|
|
|
|
2021-02-04 16:23:58 -08:00
|
|
|
/// Append a string \p str of a given length \p len, with separation type \p sep.
|
2022-10-08 12:24:27 -05:00
|
|
|
bool append(const char *str, size_t len, separation_type_t sep = separation_type_t::inferred) {
|
|
|
|
if (!try_add_size(len)) return false;
|
2018-05-29 21:11:34 -07:00
|
|
|
// Try merging with the last element.
|
2021-02-04 16:23:58 -08:00
|
|
|
if (sep == separation_type_t::inferred && last_inferred()) {
|
|
|
|
elements_.back().contents.append(str, len);
|
2018-05-29 21:11:34 -07:00
|
|
|
} else {
|
2021-02-04 16:23:58 -08:00
|
|
|
elements_.emplace_back(std::string(str, len), sep);
|
2018-05-29 21:11:34 -07:00
|
|
|
}
|
2022-10-08 12:24:27 -05:00
|
|
|
return true;
|
2018-05-29 21:11:34 -07:00
|
|
|
}
|
|
|
|
|
2021-02-04 16:23:58 -08:00
|
|
|
/// Append a string \p str with separation type \p sep.
|
2022-10-08 12:24:27 -05:00
|
|
|
bool append(std::string &&str, separation_type_t sep = separation_type_t::inferred) {
|
|
|
|
if (!try_add_size(str.size())) return false;
|
2021-02-04 16:02:40 -08:00
|
|
|
// Try merging with the last element.
|
2021-02-04 16:23:58 -08:00
|
|
|
if (sep == separation_type_t::inferred && last_inferred()) {
|
2021-02-04 16:02:40 -08:00
|
|
|
elements_.back().contents.append(str);
|
|
|
|
} else {
|
|
|
|
elements_.emplace_back(std::move(str), sep);
|
|
|
|
}
|
2022-10-08 12:24:27 -05:00
|
|
|
return true;
|
2018-05-29 21:11:34 -07:00
|
|
|
}
|
2021-02-04 16:06:28 -08:00
|
|
|
|
2021-02-04 17:02:31 -08:00
|
|
|
/// Remove all elements and unset the discard flag.
|
|
|
|
void clear() {
|
|
|
|
elements_.clear();
|
|
|
|
contents_size_ = 0;
|
|
|
|
discard_ = false;
|
|
|
|
}
|
|
|
|
|
2021-02-04 16:06:28 -08:00
|
|
|
private:
|
2021-02-04 16:23:58 -08:00
|
|
|
/// \return true if our last element has an inferred separation type.
|
|
|
|
bool last_inferred() const {
|
|
|
|
return !elements_.empty() && !elements_.back().is_explicitly_separated();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If our last element has an inferred separation, return a pointer to it; else nullptr.
|
|
|
|
/// This is useful for appending one inferred separation to another.
|
|
|
|
element_t *last_if_inferred() {
|
|
|
|
if (!elements_.empty() && !elements_.back().is_explicitly_separated()) {
|
|
|
|
return &elements_.back();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-02-04 16:06:28 -08:00
|
|
|
/// Mark that we are about to add the given size \p delta to the buffer. \return true if we
|
|
|
|
/// succeed, false if we exceed buffer_limit.
|
|
|
|
bool try_add_size(size_t delta) {
|
|
|
|
if (discard_) return false;
|
|
|
|
size_t proposed_size = contents_size_ + delta;
|
|
|
|
if ((proposed_size < delta) || (buffer_limit_ > 0 && proposed_size > buffer_limit_)) {
|
2021-02-04 17:02:31 -08:00
|
|
|
clear();
|
2021-02-04 16:06:28 -08:00
|
|
|
discard_ = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
contents_size_ = proposed_size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Limit on how much data we'll buffer. Zero means no limit.
|
|
|
|
size_t buffer_limit_;
|
|
|
|
|
|
|
|
/// Current size of all contents.
|
|
|
|
size_t contents_size_{0};
|
|
|
|
|
|
|
|
/// List of buffer elements.
|
|
|
|
std::vector<element_t> elements_;
|
|
|
|
|
|
|
|
/// True if we're discarding input because our buffer_limit has been exceeded.
|
|
|
|
bool discard_{false};
|
2018-05-29 21:11:34 -07:00
|
|
|
};
|
|
|
|
|
2016-05-01 20:32:40 -07:00
|
|
|
/// Describes what type of IO operation an io_data_t represents.
|
2019-01-31 16:05:42 -08:00
|
|
|
enum class io_mode_t { file, pipe, fd, close, bufferfill };
|
2005-10-08 21:20:51 +10:00
|
|
|
|
2016-05-01 20:32:40 -07:00
|
|
|
/// Represents an FD redirection.
|
2021-07-22 10:43:25 -07:00
|
|
|
class io_data_t : noncopyable_t, nonmovable_t {
|
2016-05-01 20:32:40 -07:00
|
|
|
protected:
|
2019-12-29 15:14:08 -08:00
|
|
|
io_data_t(io_mode_t m, int fd, int source_fd) : io_mode(m), fd(fd), source_fd(source_fd) {}
|
2013-01-15 17:39:20 +08:00
|
|
|
|
2016-05-01 20:32:40 -07:00
|
|
|
public:
|
|
|
|
/// Type of redirect.
|
2013-01-17 14:46:02 +08:00
|
|
|
const io_mode_t io_mode;
|
2019-12-29 15:14:08 -08:00
|
|
|
|
2016-05-01 20:32:40 -07:00
|
|
|
/// FD to redirect.
|
2013-02-04 20:07:16 +08:00
|
|
|
const int fd;
|
2012-11-18 16:30:30 -08:00
|
|
|
|
2019-12-29 15:14:08 -08:00
|
|
|
/// Source fd. This is dup2'd to fd, or if it is -1, then fd is closed.
|
|
|
|
/// That is, we call dup2(source_fd, fd).
|
|
|
|
const int source_fd;
|
|
|
|
|
2013-01-15 17:39:20 +08:00
|
|
|
virtual void print() const = 0;
|
|
|
|
virtual ~io_data_t() = 0;
|
2012-02-09 18:43:36 -08:00
|
|
|
};
|
2013-01-09 16:02:04 +08:00
|
|
|
|
2020-12-19 20:06:36 -08:00
|
|
|
class io_close_t final : public io_data_t {
|
2016-05-01 20:32:40 -07:00
|
|
|
public:
|
2019-12-29 15:14:08 -08:00
|
|
|
explicit io_close_t(int f) : io_data_t(io_mode_t::close, f, -1) {}
|
2013-01-09 16:02:04 +08:00
|
|
|
|
2018-02-18 18:50:35 -08:00
|
|
|
void print() const override;
|
2019-12-12 17:37:36 -08:00
|
|
|
~io_close_t() override;
|
2013-01-09 16:02:04 +08:00
|
|
|
};
|
2013-01-15 15:37:33 +08:00
|
|
|
|
2020-12-19 20:06:36 -08:00
|
|
|
class io_fd_t final : public io_data_t {
|
2016-05-01 20:32:40 -07:00
|
|
|
public:
|
2018-02-18 18:50:35 -08:00
|
|
|
void print() const override;
|
2013-01-15 15:37:33 +08:00
|
|
|
|
2019-12-12 17:37:36 -08:00
|
|
|
~io_fd_t() override;
|
|
|
|
|
2019-12-29 15:14:08 -08:00
|
|
|
/// fd to redirect specified fd to. For example, in 2>&1, source_fd is 1, and io_data_t::fd
|
|
|
|
/// is 2.
|
|
|
|
io_fd_t(int f, int source_fd) : io_data_t(io_mode_t::fd, f, source_fd) {}
|
2013-01-15 15:37:33 +08:00
|
|
|
};
|
2013-01-15 16:18:03 +08:00
|
|
|
|
2019-12-12 17:27:48 -08:00
|
|
|
/// Represents a redirection to or from an opened file.
|
2020-12-19 20:06:36 -08:00
|
|
|
class io_file_t final : public io_data_t {
|
2016-05-01 20:32:40 -07:00
|
|
|
public:
|
2018-02-18 18:50:35 -08:00
|
|
|
void print() const override;
|
2013-01-15 16:18:03 +08:00
|
|
|
|
2019-12-29 15:51:22 -08:00
|
|
|
io_file_t(int fd, autoclose_fd_t file)
|
|
|
|
: io_data_t(io_mode_t::file, fd, file.fd()), file_fd_(std::move(file)) {
|
2020-05-29 23:54:42 -05:00
|
|
|
// Invalid file redirections are replaced with a closed fd, so the following
|
|
|
|
// assertion isn't guaranteed to pass:
|
|
|
|
// assert(file_fd_.valid() && "File is not valid");
|
2019-12-29 15:51:22 -08:00
|
|
|
}
|
2019-12-12 17:27:48 -08:00
|
|
|
|
|
|
|
~io_file_t() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// The fd for the file which we are writing to or reading from.
|
|
|
|
autoclose_fd_t file_fd_;
|
2013-01-15 16:18:03 +08:00
|
|
|
};
|
2005-10-08 21:20:51 +10:00
|
|
|
|
2019-01-31 16:05:42 -08:00
|
|
|
/// Represents (one end) of a pipe.
|
2020-12-19 20:06:36 -08:00
|
|
|
class io_pipe_t final : public io_data_t {
|
2019-01-31 16:05:42 -08:00
|
|
|
// The pipe's fd. Conceptually this is dup2'd to io_data_t::fd.
|
|
|
|
autoclose_fd_t pipe_fd_;
|
2013-01-15 17:31:36 +08:00
|
|
|
|
2019-01-31 16:05:42 -08:00
|
|
|
/// Whether this is an input pipe. This is used only for informational purposes.
|
|
|
|
const bool is_input_;
|
2013-01-15 17:31:36 +08:00
|
|
|
|
2019-01-31 16:05:42 -08:00
|
|
|
public:
|
2019-01-31 16:05:42 -08:00
|
|
|
void print() const override;
|
|
|
|
|
2019-01-31 16:05:42 -08:00
|
|
|
io_pipe_t(int fd, bool is_input, autoclose_fd_t pipe_fd)
|
2019-12-29 15:14:08 -08:00
|
|
|
: io_data_t(io_mode_t::pipe, fd, pipe_fd.fd()),
|
|
|
|
pipe_fd_(std::move(pipe_fd)),
|
2019-12-29 15:51:22 -08:00
|
|
|
is_input_(is_input) {
|
|
|
|
assert(pipe_fd_.valid() && "Pipe is not valid");
|
|
|
|
}
|
2019-01-31 16:05:42 -08:00
|
|
|
|
2019-11-19 22:17:30 -08:00
|
|
|
~io_pipe_t() override;
|
2019-01-31 16:05:42 -08:00
|
|
|
};
|
|
|
|
|
2019-01-31 16:05:42 -08:00
|
|
|
class io_buffer_t;
|
|
|
|
|
|
|
|
/// Represents filling an io_buffer_t. Very similar to io_pipe_t.
|
2020-12-19 20:06:36 -08:00
|
|
|
class io_bufferfill_t final : public io_data_t {
|
2019-01-31 16:05:42 -08:00
|
|
|
/// Write end. The other end is connected to an io_buffer_t.
|
|
|
|
const autoclose_fd_t write_fd_;
|
|
|
|
|
|
|
|
/// The receiving buffer.
|
|
|
|
const std::shared_ptr<io_buffer_t> buffer_;
|
|
|
|
|
|
|
|
public:
|
|
|
|
void print() const override;
|
|
|
|
|
2019-02-01 01:58:06 -08:00
|
|
|
// The ctor is public to support make_shared() in the static create function below.
|
|
|
|
// Do not invoke this directly.
|
2020-04-25 19:15:08 -07:00
|
|
|
io_bufferfill_t(int target, autoclose_fd_t write_fd, std::shared_ptr<io_buffer_t> buffer)
|
|
|
|
: io_data_t(io_mode_t::bufferfill, target, write_fd.fd()),
|
2019-01-31 16:05:42 -08:00
|
|
|
write_fd_(std::move(write_fd)),
|
2019-12-29 15:51:22 -08:00
|
|
|
buffer_(std::move(buffer)) {
|
|
|
|
assert(write_fd_.valid() && "fd is not valid");
|
|
|
|
}
|
2019-01-31 16:05:42 -08:00
|
|
|
|
2019-11-19 22:17:30 -08:00
|
|
|
~io_bufferfill_t() override;
|
2019-01-31 16:05:42 -08:00
|
|
|
|
|
|
|
std::shared_ptr<io_buffer_t> buffer() const { return buffer_; }
|
|
|
|
|
2019-02-01 01:58:06 -08:00
|
|
|
/// Create an io_bufferfill_t which, when written from, fills a buffer with the contents.
|
2019-01-31 16:05:42 -08:00
|
|
|
/// \returns nullptr on failure, e.g. too many open fds.
|
|
|
|
///
|
2020-04-25 19:15:08 -07:00
|
|
|
/// \param target the fd which this will be dup2'd to - typically stdout.
|
2021-02-05 18:14:50 -08:00
|
|
|
static shared_ptr<io_bufferfill_t> create(size_t buffer_limit = 0, int target = STDOUT_FILENO);
|
2019-02-01 01:58:06 -08:00
|
|
|
|
|
|
|
/// Reset the receiver (possibly closing the write end of the pipe), and complete the fillthread
|
|
|
|
/// of the buffer. \return the buffer.
|
2021-02-04 17:13:15 -08:00
|
|
|
static separated_buffer_t finish(std::shared_ptr<io_bufferfill_t> &&filler);
|
2019-01-31 16:05:42 -08:00
|
|
|
};
|
|
|
|
|
2023-02-17 19:21:44 -06:00
|
|
|
struct callback_args_t;
|
|
|
|
struct autoclose_fd_t2;
|
|
|
|
|
2019-11-10 14:00:30 -08:00
|
|
|
using io_data_ref_t = std::shared_ptr<const io_data_t>;
|
|
|
|
|
2023-10-08 23:22:27 +02:00
|
|
|
using io_chain_t = IoChain;
|
2005-10-08 21:20:51 +10:00
|
|
|
|
2023-02-04 11:21:42 +01:00
|
|
|
dup2_list_t dup2_list_resolve_chain_shim(const io_chain_t &io_chain);
|
|
|
|
|
2005-10-08 21:20:51 +10:00
|
|
|
#endif
|