mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-28 05:43:11 +00:00
Change readch()
into try_readch()
This lets us call into the entirety of the prior `readch()` with an exhaustible input stream without panicking on the `unreachable!()` call. The previous functionality is kept under the old name by calling `try_readch()` with the `blocking` parameter set to `true` (100% same behavior as before). While the `try_readch(false)` entrypoint isn't used directly by the current fish codebase, it is required in order to automate input reader tests without the overhead and complexity of running the test harness in a tty emulator emulator like pexpect or tmux, which moreover necessitates out-of-process testing – which is incompatible with most perf-guided testing harnesses. I hope to be able to upstream harness integrations using this entry point in the near future.
This commit is contained in:
parent
b3108c0cee
commit
b92830cb17
1 changed files with 26 additions and 10 deletions
|
@ -593,28 +593,44 @@ pub trait InputEventQueuer {
|
||||||
self.get_input_data_mut().queue.pop_front()
|
self.get_input_data_mut().queue.pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Function used by input_readch to read bytes from stdin until enough bytes have been read to
|
/// An "infallible" version of [`try_readch`](Self::try_readch) to be used when the input pipe
|
||||||
/// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously
|
/// fd is expected to outlive the input reader. Will panic upon EOF.
|
||||||
/// been read and then 'unread' using \c input_common_unreadch, that character is returned.
|
#[inline(always)]
|
||||||
fn readch(&mut self) -> CharEvent {
|
fn readch(&mut self) -> CharEvent {
|
||||||
|
match self.try_readch(/*blocking*/ true) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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', that character is returned.
|
||||||
|
///
|
||||||
|
/// This is guaranteed to keep returning `Some(CharEvent)` so long as the input stream remains
|
||||||
|
/// open; `None` is only returned upon EOF as the main loop within blocks until input becomes
|
||||||
|
/// available.
|
||||||
|
///
|
||||||
|
/// This method is used directly by the fuzzing harness to avoid a panic on bounded inputs.
|
||||||
|
fn try_readch(&mut self, blocking: bool) -> Option<CharEvent> {
|
||||||
loop {
|
loop {
|
||||||
// Do we have something enqueued already?
|
// Do we have something enqueued already?
|
||||||
// Note this may be initially true, or it may become true through calls to
|
// Note this may be initially true, or it may become true through calls to
|
||||||
// iothread_service_main() or env_universal_barrier() below.
|
// iothread_service_main() or env_universal_barrier() below.
|
||||||
if let Some(mevt) = self.try_pop() {
|
if let Some(mevt) = self.try_pop() {
|
||||||
return mevt;
|
return Some(mevt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are going to block; but first allow any override to inject events.
|
// We are going to block; but first allow any override to inject events.
|
||||||
self.prepare_to_select();
|
self.prepare_to_select();
|
||||||
if let Some(mevt) = self.try_pop() {
|
if let Some(mevt) = self.try_pop() {
|
||||||
return mevt;
|
return Some(mevt);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rr = readb(self.get_in_fd(), /*blocking=*/ true);
|
let rr = readb(self.get_in_fd(), blocking);
|
||||||
match rr {
|
match rr {
|
||||||
ReadbResult::Eof => {
|
ReadbResult::Eof => {
|
||||||
return CharEvent::Eof;
|
return Some(CharEvent::Eof);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadbResult::Interrupted => {
|
ReadbResult::Interrupted => {
|
||||||
|
@ -682,16 +698,16 @@ pub trait InputEventQueuer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return if let Some(key) = key {
|
return if let Some(key) = key {
|
||||||
CharEvent::from_key_seq(key, seq)
|
Some(CharEvent::from_key_seq(key, seq))
|
||||||
} else {
|
} else {
|
||||||
self.insert_front(seq.chars().skip(1).map(CharEvent::from_char));
|
self.insert_front(seq.chars().skip(1).map(CharEvent::from_char));
|
||||||
let Some(c) = seq.chars().next() else {
|
let Some(c) = seq.chars().next() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
CharEvent::from_key_seq(Key::from_raw(c), seq)
|
Some(CharEvent::from_key_seq(Key::from_raw(c), seq))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
ReadbResult::NothingToRead => unreachable!(),
|
ReadbResult::NothingToRead => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue