Let read read from fds other than 0

This allows

read </dev/tty

to work.

Fixes #7358
This commit is contained in:
Fabian Homborg
2020-09-30 18:29:00 +02:00
parent ca0f5686ff
commit 0951a706cf
7 changed files with 38 additions and 20 deletions

View File

@@ -30,6 +30,7 @@
#include "parser.h"
#include "proc.h"
#include "reader.h"
#include "termios.h"
#include "wcstringutil.h"
#include "wgetopt.h"
#include "wutil.h" // IWYU pragma: keep
@@ -198,7 +199,7 @@ static int parse_cmd_opts(read_cmd_opts_t &opts, int *optind, //!OCLINT(high nc
/// we weren't asked to split on null characters.
static int read_interactive(parser_t &parser, wcstring &buff, int nchars, bool shell, bool silent,
const wchar_t *prompt, const wchar_t *right_prompt,
const wchar_t *commandline) {
const wchar_t *commandline, int stdin) {
int exit_res = STATUS_CMD_OK;
// Construct a configuration.
@@ -217,6 +218,8 @@ static int read_interactive(parser_t &parser, wcstring &buff, int nchars, bool s
conf.left_prompt_cmd = prompt;
conf.right_prompt_cmd = right_prompt;
conf.stdin = stdin;
// Don't keep history.
reader_push(parser, wcstring{}, std::move(conf));
reader_get_history()->resolve_pending();
@@ -487,7 +490,7 @@ maybe_t<int> builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **arg
if (stream_stdin_is_a_tty && !opts.split_null) {
// Read interactively using reader_readline(). This does not support splitting on null.
exit_res = read_interactive(parser, buff, opts.nchars, opts.shell, opts.silent,
opts.prompt, opts.right_prompt, opts.commandline);
opts.prompt, opts.right_prompt, opts.commandline, streams.stdin_fd);
} else if (!opts.nchars && !stream_stdin_is_a_tty &&
lseek(streams.stdin_fd, 0, SEEK_CUR) != -1) {
exit_res = read_in_chunks(streams.stdin_fd, buff, opts.split_null);

View File

@@ -318,7 +318,7 @@ void init_input() {
}
}
inputter_t::inputter_t(parser_t &parser) : parser_(parser.shared()) {}
inputter_t::inputter_t(parser_t &parser, int stdin) : event_queue_(stdin), parser_(parser.shared()) {}
void inputter_t::function_push_arg(wchar_t arg) { input_function_args_.push_back(arg); }

View File

@@ -23,7 +23,7 @@ void init_input();
struct input_mapping_t;
class inputter_t {
input_event_queue_t event_queue_{};
input_event_queue_t event_queue_;
std::vector<wchar_t> input_function_args_{};
bool function_status_{false};
@@ -37,11 +37,12 @@ class inputter_t {
bool mapping_is_match(const input_mapping_t &m);
maybe_t<input_mapping_t> find_mapping();
char_event_t read_characters_no_readline();
int stdin{0};
public:
inputter_t(parser_t &parser);
inputter_t(parser_t &parser, int stdin = 0);
/// Read a character from fd 0. Try to convert some escape sequences into character constants,
/// Read a character from stdin. Try to convert some escape sequences into character constants,
/// but do not permanently block the escape character.
///
/// This is performed in the same way vim does it, i.e. if an escape character is read, wait for

View File

@@ -46,12 +46,12 @@ void input_common_init(interrupt_func_t func) { interrupt_handler = func; }
char_event_t input_event_queue_t::readb() {
for (;;) {
fd_set fdset;
int fd_max = 0;
int fd_max = stdin;
int ioport = iothread_port();
int res;
FD_ZERO(&fdset);
FD_SET(0, &fdset);
FD_SET(stdin, &fdset);
if (ioport > 0) {
FD_SET(ioport, &fdset);
fd_max = std::max(fd_max, ioport);
@@ -106,9 +106,9 @@ char_event_t input_event_queue_t::readb() {
}
}
if (FD_ISSET(STDIN_FILENO, &fdset)) {
if (FD_ISSET(stdin, &fdset)) {
unsigned char arr[1];
if (read_blocked(0, arr, 1) != 1) {
if (read_blocked(stdin, arr, 1) != 1) {
// The teminal has been closed.
return char_event_type_t::eof;
}
@@ -212,7 +212,7 @@ char_event_t input_event_queue_t::readch_timed(bool dequeue_timeouts) {
} else {
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
FD_SET(stdin, &fds);
struct timeval tm = {wait_on_escape_ms / 1000, 1000 * (wait_on_escape_ms % 1000)};
if (select(1, &fds, nullptr, nullptr, &tm) > 0) {
result = readch();

View File

@@ -193,7 +193,10 @@ class input_event_queue_t {
char_event_t readb();
int stdin{0};
public:
input_event_queue_t(int in = 0) : stdin(in) {};
/// Function used by input_readch to read bytes from stdin until enough bytes have been read to
/// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously
/// been read and then 'unread' using \c input_common_unreadch, that character is returned. This

View File

@@ -609,7 +609,7 @@ class reader_data_t : public std::enable_shared_from_this<reader_data_t> {
reader_data_t(std::shared_ptr<parser_t> parser, history_t *hist, reader_config_t &&conf)
: conf(std::move(conf)),
parser_ref(std::move(parser)),
inputter(*parser_ref),
inputter(*parser_ref, conf.stdin),
history(hist) {}
void update_buff_pos(editable_line_t *el, maybe_t<size_t> new_pos = none_t());
@@ -2122,7 +2122,7 @@ static void reader_interactive_init(parser_t &parser) {
}
// Configure terminal attributes
if (tcsetattr(0, TCSANOW, &shell_modes) == -1) {
if (tcsetattr(STDIN_FILENO, TCSANOW, &shell_modes) == -1) {
if (errno == EIO) {
redirect_tty_output();
}
@@ -2700,7 +2700,7 @@ maybe_t<char_event_t> reader_data_t::read_normal_chars(readline_loop_state_t &rl
while (accumulated_chars.size() < limit) {
bool allow_commands = (accumulated_chars.empty());
auto evt = inputter.readch(allow_commands);
if (!event_is_normal_char(evt) || !can_read(STDIN_FILENO)) {
if (!event_is_normal_char(evt) || !can_read(conf.stdin)) {
event_needing_handling = std::move(evt);
break;
} else if (evt.input_style == char_input_style_t::notfirst && accumulated_chars.empty() &&
@@ -3636,8 +3636,10 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
/// A helper that kicks off syntax highlighting, autosuggestion computing, and repaints.
auto color_suggest_repaint_now = [this] {
this->update_autosuggestion();
this->super_highlight_me_plenty();
if (conf.stdin == STDIN_FILENO) {
this->update_autosuggestion();
this->super_highlight_me_plenty();
}
if (this->is_repaint_needed()) this->layout_and_repaint(L"toplevel");
this->force_exec_prompt_and_repaint = false;
};
@@ -3646,10 +3648,10 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
force_exec_prompt_and_repaint = true;
// Get the current terminal modes. These will be restored when the function returns.
if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output();
if (tcgetattr(conf.stdin, &old_modes) == -1 && errno == EIO) redirect_tty_output();
// Set the new modes.
if (tcsetattr(0, TCSANOW, &shell_modes) == -1) {
if (tcsetattr(conf.stdin, TCSANOW, &shell_modes) == -1) {
int err = errno;
if (err == EIO) {
redirect_tty_output();
@@ -3756,7 +3758,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
// Redraw the command line. This is what ensures the autosuggestion is hidden, etc. after the
// user presses enter.
if (this->is_repaint_needed()) this->layout_and_repaint(L"prepare to execute");
if (this->is_repaint_needed() || conf.stdin != STDIN_FILENO) this->layout_and_repaint(L"prepare to execute");
// Emit a newline so that the output is on the line after the command.
// But do not emit a newline if the cursor has wrapped onto a new line all its own - see #6826.
@@ -3764,6 +3766,12 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
ignore_result(write(STDOUT_FILENO, "\n", 1));
}
// HACK: If stdin isn't the same terminal as stdout, we just moved the cursor.
// For now, just reset it to the beginning of the line.
if (conf.stdin != STDIN_FILENO) {
ignore_result(write(STDOUT_FILENO, "\r", 1));
}
// Ensure we have no pager contents when we exit.
if (!pager.empty()) {
// Clear to end of screen to erase the pager contents.
@@ -3775,7 +3783,7 @@ maybe_t<wcstring> reader_data_t::readline(int nchars_or_0) {
if (s_exit_state != exit_state_t::finished_handlers) {
// The order of the two conditions below is important. Try to restore the mode
// in all cases, but only complain if interactive.
if (tcsetattr(0, TCSANOW, &old_modes) == -1 &&
if (tcsetattr(conf.stdin, TCSANOW, &old_modes) == -1 &&
session_interactivity() != session_interactivity_t::not_interactive) {
if (errno == EIO) redirect_tty_output();
wperror(L"tcsetattr"); // return to previous mode

View File

@@ -214,6 +214,9 @@ struct reader_config_t {
/// If set, do not show what is typed.
bool in_silent_mode{false};
/// The fd for stdin, default to actual stdin.
int stdin{0};
};
/// Push a new reader environment controlled by \p conf.