1#[cfg(feature = "buffer-redux")]
3use buffer_redux::BufReader;
4use std::cmp;
5use std::collections::HashMap;
6use std::fs::{File, OpenOptions};
7#[cfg(not(feature = "buffer-redux"))]
8use std::io::BufReader;
9use std::io::{self, ErrorKind, Read, Write};
10use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, RawFd};
11use std::os::unix::net::UnixStream;
12use std::sync::atomic::{AtomicBool, Ordering};
13use std::sync::mpsc::{self, SyncSender};
14use std::sync::{Arc, Mutex};
15
16use log::{debug, warn};
17use nix::errno::Errno;
18use nix::poll::{self, PollFlags, PollTimeout};
19use nix::sys::select::{self, FdSet};
20#[cfg(not(feature = "termios"))]
21use nix::sys::termios::Termios;
22use nix::unistd::{close, isatty, read, write};
23#[cfg(feature = "termios")]
24use termios::Termios;
25use unicode_segmentation::UnicodeSegmentation;
26use utf8parse::{Parser, Receiver};
27
28use super::{width, Event, RawMode, RawReader, Renderer, Term};
29use crate::config::{Behavior, BellStyle, ColorMode, Config};
30use crate::highlight::Highlighter;
31use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
32use crate::layout::{Layout, Position};
33use crate::line_buffer::LineBuffer;
34use crate::{error, Cmd, ReadlineError, Result};
35
36const UNSUPPORTED_TERM: [&str; 3] = ["dumb", "cons25", "emacs"];
38
39const BRACKETED_PASTE_ON: &str = "\x1b[?2004h";
40const BRACKETED_PASTE_OFF: &str = "\x1b[?2004l";
41
42nix::ioctl_read_bad!(win_size, libc::TIOCGWINSZ, libc::winsize);
43
44#[allow(clippy::useless_conversion)]
45fn get_win_size(fd: RawFd) -> (usize, usize) {
46 use std::mem::zeroed;
47
48 if cfg!(test) {
49 return (80, 24);
50 }
51
52 unsafe {
53 let mut size: libc::winsize = zeroed();
54 match win_size(fd, &mut size) {
55 Ok(0) => {
56 let cols = if size.ws_col == 0 {
61 80
62 } else {
63 size.ws_col as usize
64 };
65 let rows = if size.ws_row == 0 {
66 usize::MAX
67 } else {
68 size.ws_row as usize
69 };
70 (cols, rows)
71 }
72 _ => (80, 24),
73 }
74 }
75}
76
77fn is_unsupported_term() -> bool {
80 match std::env::var("TERM") {
81 Ok(term) => {
82 for iter in &UNSUPPORTED_TERM {
83 if (*iter).eq_ignore_ascii_case(&term) {
84 return true;
85 }
86 }
87 false
88 }
89 Err(_) => false,
90 }
91}
92
93fn is_a_tty(fd: RawFd) -> bool {
95 isatty(fd).unwrap_or(false)
96}
97
98#[cfg(any(not(feature = "buffer-redux"), test))]
99pub type PosixBuffer = ();
100#[cfg(all(feature = "buffer-redux", not(test)))]
101pub type PosixBuffer = buffer_redux::Buffer;
102#[cfg(not(test))]
103pub type Buffer = PosixBuffer;
104
105pub type PosixKeyMap = HashMap<KeyEvent, Cmd>;
106#[cfg(not(test))]
107pub type KeyMap = PosixKeyMap;
108
109#[must_use = "You must restore default mode (disable_raw_mode)"]
110pub struct PosixMode {
111 termios: Termios,
112 tty_in: RawFd,
113 tty_out: Option<RawFd>,
114 raw_mode: Arc<AtomicBool>,
115}
116
117#[cfg(not(test))]
118pub type Mode = PosixMode;
119
120impl RawMode for PosixMode {
121 fn disable_raw_mode(&self) -> Result<()> {
123 termios_::disable_raw_mode(self.tty_in, &self.termios)?;
124 if let Some(out) = self.tty_out {
126 write_all(out, BRACKETED_PASTE_OFF)?;
127 }
128 self.raw_mode.store(false, Ordering::SeqCst);
129 Ok(())
130 }
131}
132
133struct TtyIn {
136 fd: RawFd,
137 sigwinch_pipe: Option<RawFd>,
138}
139
140impl Read for TtyIn {
141 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
142 loop {
143 let res = unsafe {
144 libc::read(
145 self.fd,
146 buf.as_mut_ptr() as *mut libc::c_void,
147 buf.len() as libc::size_t,
148 )
149 };
150 if res == -1 {
151 let error = io::Error::last_os_error();
152 if error.kind() == ErrorKind::Interrupted && self.sigwinch()? {
153 return Err(io::Error::new(
154 ErrorKind::Interrupted,
155 error::WindowResizedError,
156 ));
157 } else if error.kind() != ErrorKind::Interrupted {
158 return Err(error);
159 }
160 } else {
161 #[allow(clippy::cast_sign_loss)]
162 return Ok(res as usize);
163 }
164 }
165 }
166}
167
168impl TtyIn {
169 fn sigwinch(&self) -> nix::Result<bool> {
171 if let Some(pipe) = self.sigwinch_pipe {
172 let mut buf = [0u8; 64];
173 match read(pipe, &mut buf) {
174 Ok(0) => Ok(false),
175 Ok(_) => Ok(true),
176 Err(e) if e == Errno::EWOULDBLOCK || e == Errno::EINTR => Ok(false),
177 Err(e) => Err(e),
178 }
179 } else {
180 Ok(false)
181 }
182 }
183}
184
185type PipeReader = Arc<Mutex<(File, mpsc::Receiver<String>)>>;
187type PipeWriter = (Arc<Mutex<File>>, SyncSender<String>);
189
190pub struct PosixRawReader {
192 tty_in: BufReader<TtyIn>,
193 timeout_ms: PollTimeout,
194 parser: Parser,
195 key_map: PosixKeyMap,
196 pipe_reader: Option<PipeReader>,
198}
199
200impl AsFd for PosixRawReader {
201 fn as_fd(&self) -> BorrowedFd<'_> {
202 let fd = self.tty_in.get_ref().fd;
203 unsafe { BorrowedFd::borrow_raw(fd) }
204 }
205}
206
207struct Utf8 {
208 c: Option<char>,
209 valid: bool,
210}
211
212const UP: char = 'A'; const DOWN: char = 'B'; const RIGHT: char = 'C'; const LEFT: char = 'D'; const END: char = 'F'; const HOME: char = 'H'; const INSERT: char = '2'; const DELETE: char = '3'; const PAGE_UP: char = '5'; const PAGE_DOWN: char = '6'; const RXVT_HOME: char = '7';
224const RXVT_END: char = '8';
225
226const SHIFT: char = '2';
227const ALT: char = '3';
228const ALT_SHIFT: char = '4';
229const CTRL: char = '5';
230const CTRL_SHIFT: char = '6';
231const CTRL_ALT: char = '7';
232const CTRL_ALT_SHIFT: char = '8';
233
234const RXVT_SHIFT: char = '$';
235const RXVT_CTRL: char = '\x1e';
236const RXVT_CTRL_SHIFT: char = '@';
237
238impl PosixRawReader {
239 fn new(
240 fd: RawFd,
241 sigwinch_pipe: Option<RawFd>,
242 buffer: Option<PosixBuffer>,
243 config: &Config,
244 key_map: PosixKeyMap,
245 pipe_reader: Option<PipeReader>,
246 ) -> Self {
247 let inner = TtyIn { fd, sigwinch_pipe };
248 #[cfg(any(not(feature = "buffer-redux"), test))]
249 let (tty_in, _) = (BufReader::with_capacity(1024, inner), buffer);
250 #[cfg(all(feature = "buffer-redux", not(test)))]
251 let tty_in = if let Some(buffer) = buffer {
252 BufReader::with_buffer(buffer, inner)
253 } else {
254 BufReader::with_capacity(1024, inner)
255 };
256 Self {
257 tty_in,
258 timeout_ms: config.keyseq_timeout().into(),
259 parser: Parser::new(),
260 key_map,
261 pipe_reader,
262 }
263 }
264
265 fn escape_sequence(&mut self) -> Result<KeyEvent> {
268 self._do_escape_sequence(true)
269 }
270
271 fn _do_escape_sequence(&mut self, allow_recurse: bool) -> Result<KeyEvent> {
273 let seq1 = self.next_char()?;
275 if seq1 == '[' {
276 self.escape_csi()
278 } else if seq1 == 'O' {
279 self.escape_o()
282 } else if seq1 == '\x1b' {
283 if !allow_recurse {
299 return Ok(E::ESC);
300 }
301 let timeout = if self.timeout_ms.is_none() {
302 100u8.into()
303 } else {
304 self.timeout_ms
305 };
306 match self.poll(timeout) {
307 Ok(0) | Err(_) => Ok(E::ESC),
310 Ok(n) => {
311 debug_assert!(n > 0, "{}", n);
312 let E(k, m) = self._do_escape_sequence(false)?;
314 Ok(E(k, m | M::ALT))
315 }
316 }
317 } else {
318 Ok(E::alt(seq1))
319 }
320 }
321
322 fn escape_csi(&mut self) -> Result<KeyEvent> {
324 let seq2 = self.next_char()?;
325 if seq2.is_ascii_digit() {
326 match seq2 {
327 '0' | '9' => {
328 debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2);
329 Ok(E(K::UnknownEscSeq, M::NONE))
330 }
331 _ => {
332 self.extended_escape(seq2)
334 }
335 }
336 } else if seq2 == '[' {
337 let seq3 = self.next_char()?;
338 Ok(match seq3 {
340 'A' => E(K::F(1), M::NONE),
341 'B' => E(K::F(2), M::NONE),
342 'C' => E(K::F(3), M::NONE),
343 'D' => E(K::F(4), M::NONE),
344 'E' => E(K::F(5), M::NONE),
345 _ => {
346 debug!(target: "rustyline", "unsupported esc sequence: \\E[[{:?}", seq3);
347 E(K::UnknownEscSeq, M::NONE)
348 }
349 })
350 } else {
351 Ok(match seq2 {
353 UP => E(K::Up, M::NONE),
354 DOWN => E(K::Down, M::NONE),
355 RIGHT => E(K::Right, M::NONE),
356 LEFT => E(K::Left, M::NONE),
357 END => E(K::End, M::NONE),
359 HOME => E(K::Home, M::NONE), 'Z' => E(K::BackTab, M::NONE),
367 'a' => E(K::Up, M::SHIFT), 'b' => E(K::Down, M::SHIFT), 'c' => E(K::Right, M::SHIFT), 'd' => E(K::Left, M::SHIFT), _ => {
372 debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2);
373 E(K::UnknownEscSeq, M::NONE)
374 }
375 })
376 }
377 }
378
379 #[allow(clippy::cognitive_complexity)]
381 fn extended_escape(&mut self, seq2: char) -> Result<KeyEvent> {
382 let seq3 = self.next_char()?;
383 if seq3 == '~' {
384 Ok(match seq2 {
385 '1' | RXVT_HOME => E(K::Home, M::NONE), INSERT => E(K::Insert, M::NONE),
387 DELETE => E(K::Delete, M::NONE),
388 '4' | RXVT_END => E(K::End, M::NONE), PAGE_UP => E(K::PageUp, M::NONE),
390 PAGE_DOWN => E(K::PageDown, M::NONE),
391 _ => {
392 debug!(target: "rustyline",
393 "unsupported esc sequence: \\E[{}~", seq2);
394 E(K::UnknownEscSeq, M::NONE)
395 }
396 })
397 } else if seq3.is_ascii_digit() {
398 let seq4 = self.next_char()?;
399 if seq4 == '~' {
400 Ok(match (seq2, seq3) {
401 ('1', '1') => E(K::F(1), M::NONE), ('1', '2') => E(K::F(2), M::NONE), ('1', '3') => E(K::F(3), M::NONE), ('1', '4') => E(K::F(4), M::NONE), ('1', '5') => E(K::F(5), M::NONE), ('1', '7') => E(K::F(6), M::NONE), ('1', '8') => E(K::F(7), M::NONE), ('1', '9') => E(K::F(8), M::NONE), ('2', '0') => E(K::F(9), M::NONE), ('2', '1') => E(K::F(10), M::NONE), ('2', '3') => E(K::F(11), M::NONE), ('2', '4') => E(K::F(12), M::NONE), _ => {
416 debug!(target: "rustyline",
417 "unsupported esc sequence: \\E[{}{}~", seq2, seq3);
418 E(K::UnknownEscSeq, M::NONE)
419 }
420 })
421 } else if seq4 == ';' {
422 let seq5 = self.next_char()?;
423 if seq5.is_ascii_digit() {
424 let seq6 = self.next_char()?;
425 if seq6.is_ascii_digit() {
426 self.next_char()?; Ok(E(K::UnknownEscSeq, M::NONE))
428 } else if seq6 == 'R' {
429 Ok(E(K::UnknownEscSeq, M::NONE))
430 } else if seq6 == '~' {
431 Ok(match (seq2, seq3, seq5) {
432 ('1', '5', CTRL) => E(K::F(5), M::CTRL),
433 ('1', '7', CTRL) => E(K::F(6), M::CTRL),
435 ('1', '8', CTRL) => E(K::F(7), M::CTRL),
437 ('1', '9', CTRL) => E(K::F(8), M::CTRL),
438 ('2', '0', CTRL) => E(K::F(9), M::CTRL),
440 ('2', '1', CTRL) => E(K::F(10), M::CTRL),
442 ('2', '3', CTRL) => E(K::F(11), M::CTRL),
444 ('2', '4', CTRL) => E(K::F(12), M::CTRL),
446 _ => {
448 debug!(target: "rustyline",
449 "unsupported esc sequence: \\E[{}{};{}~", seq2, seq3, seq5);
450 E(K::UnknownEscSeq, M::NONE)
451 }
452 })
453 } else {
454 debug!(target: "rustyline",
455 "unsupported esc sequence: \\E[{}{};{}{}", seq2, seq3, seq5, seq6);
456 Ok(E(K::UnknownEscSeq, M::NONE))
457 }
458 } else {
459 debug!(target: "rustyline",
460 "unsupported esc sequence: \\E[{}{};{:?}", seq2, seq3, seq5);
461 Ok(E(K::UnknownEscSeq, M::NONE))
462 }
463 } else if seq4.is_ascii_digit() {
464 let seq5 = self.next_char()?;
465 if seq5 == '~' {
466 Ok(match (seq2, seq3, seq4) {
467 ('2', '0', '0') => E(K::BracketedPasteStart, M::NONE),
468 ('2', '0', '1') => E(K::BracketedPasteEnd, M::NONE),
469 _ => {
470 debug!(target: "rustyline",
471 "unsupported esc sequence: \\E[{}{}{}~", seq2, seq3, seq4);
472 E(K::UnknownEscSeq, M::NONE)
473 }
474 })
475 } else {
476 debug!(target: "rustyline",
477 "unsupported esc sequence: \\E[{}{}{}{}", seq2, seq3, seq4, seq5);
478 Ok(E(K::UnknownEscSeq, M::NONE))
479 }
480 } else {
481 debug!(target: "rustyline",
482 "unsupported esc sequence: \\E[{}{}{:?}", seq2, seq3, seq4);
483 Ok(E(K::UnknownEscSeq, M::NONE))
484 }
485 } else if seq3 == ';' {
486 let seq4 = self.next_char()?;
487 if seq4.is_ascii_digit() {
488 let seq5 = self.next_char()?;
489 if seq5.is_ascii_digit() {
490 self.next_char()?; Ok(E(K::UnknownEscSeq, M::NONE))
493 } else if seq2 == '1' {
494 Ok(match (seq4, seq5) {
495 (SHIFT, UP) => E(K::Up, M::SHIFT), (SHIFT, DOWN) => E(K::Down, M::SHIFT), (SHIFT, RIGHT) => E(K::Right, M::SHIFT),
498 (SHIFT, LEFT) => E(K::Left, M::SHIFT),
499 (SHIFT, END) => E(K::End, M::SHIFT), (SHIFT, HOME) => E(K::Home, M::SHIFT), (ALT, UP) => E(K::Up, M::ALT),
505 (ALT, DOWN) => E(K::Down, M::ALT),
506 (ALT, RIGHT) => E(K::Right, M::ALT),
507 (ALT, LEFT) => E(K::Left, M::ALT),
508 (ALT, END) => E(K::End, M::ALT),
509 (ALT, HOME) => E(K::Home, M::ALT),
510 (ALT_SHIFT, UP) => E(K::Up, M::ALT_SHIFT),
511 (ALT_SHIFT, DOWN) => E(K::Down, M::ALT_SHIFT),
512 (ALT_SHIFT, RIGHT) => E(K::Right, M::ALT_SHIFT),
513 (ALT_SHIFT, LEFT) => E(K::Left, M::ALT_SHIFT),
514 (ALT_SHIFT, END) => E(K::End, M::ALT_SHIFT),
515 (ALT_SHIFT, HOME) => E(K::Home, M::ALT_SHIFT),
516 (CTRL, UP) => E(K::Up, M::CTRL),
517 (CTRL, DOWN) => E(K::Down, M::CTRL),
518 (CTRL, RIGHT) => E(K::Right, M::CTRL),
519 (CTRL, LEFT) => E(K::Left, M::CTRL),
520 (CTRL, END) => E(K::End, M::CTRL),
521 (CTRL, HOME) => E(K::Home, M::CTRL),
522 (CTRL, 'P') => E(K::F(1), M::CTRL),
523 (CTRL, 'Q') => E(K::F(2), M::CTRL),
524 (CTRL, 'S') => E(K::F(4), M::CTRL),
525 (CTRL, 'p') => E(K::Char('0'), M::CTRL),
526 (CTRL, 'q') => E(K::Char('1'), M::CTRL),
527 (CTRL, 'r') => E(K::Char('2'), M::CTRL),
528 (CTRL, 's') => E(K::Char('3'), M::CTRL),
529 (CTRL, 't') => E(K::Char('4'), M::CTRL),
530 (CTRL, 'u') => E(K::Char('5'), M::CTRL),
531 (CTRL, 'v') => E(K::Char('6'), M::CTRL),
532 (CTRL, 'w') => E(K::Char('7'), M::CTRL),
533 (CTRL, 'x') => E(K::Char('8'), M::CTRL),
534 (CTRL, 'y') => E(K::Char('9'), M::CTRL),
535 (CTRL_SHIFT, UP) => E(K::Up, M::CTRL_SHIFT),
536 (CTRL_SHIFT, DOWN) => E(K::Down, M::CTRL_SHIFT),
537 (CTRL_SHIFT, RIGHT) => E(K::Right, M::CTRL_SHIFT),
538 (CTRL_SHIFT, LEFT) => E(K::Left, M::CTRL_SHIFT),
539 (CTRL_SHIFT, END) => E(K::End, M::CTRL_SHIFT),
540 (CTRL_SHIFT, HOME) => E(K::Home, M::CTRL_SHIFT),
541 (CTRL_SHIFT, 'p') => E(K::Char('0'), M::CTRL_SHIFT),
545 (CTRL_SHIFT, 'q') => E(K::Char('1'), M::CTRL_SHIFT),
546 (CTRL_SHIFT, 'r') => E(K::Char('2'), M::CTRL_SHIFT),
547 (CTRL_SHIFT, 's') => E(K::Char('3'), M::CTRL_SHIFT),
548 (CTRL_SHIFT, 't') => E(K::Char('4'), M::CTRL_SHIFT),
549 (CTRL_SHIFT, 'u') => E(K::Char('5'), M::CTRL_SHIFT),
550 (CTRL_SHIFT, 'v') => E(K::Char('6'), M::CTRL_SHIFT),
551 (CTRL_SHIFT, 'w') => E(K::Char('7'), M::CTRL_SHIFT),
552 (CTRL_SHIFT, 'x') => E(K::Char('8'), M::CTRL_SHIFT),
553 (CTRL_SHIFT, 'y') => E(K::Char('9'), M::CTRL_SHIFT),
554 (CTRL_ALT, UP) => E(K::Up, M::CTRL_ALT),
555 (CTRL_ALT, DOWN) => E(K::Down, M::CTRL_ALT),
556 (CTRL_ALT, RIGHT) => E(K::Right, M::CTRL_ALT),
557 (CTRL_ALT, LEFT) => E(K::Left, M::CTRL_ALT),
558 (CTRL_ALT, END) => E(K::End, M::CTRL_ALT),
559 (CTRL_ALT, HOME) => E(K::Home, M::CTRL_ALT),
560 (CTRL_ALT, 'p') => E(K::Char('0'), M::CTRL_ALT),
561 (CTRL_ALT, 'q') => E(K::Char('1'), M::CTRL_ALT),
562 (CTRL_ALT, 'r') => E(K::Char('2'), M::CTRL_ALT),
563 (CTRL_ALT, 's') => E(K::Char('3'), M::CTRL_ALT),
564 (CTRL_ALT, 't') => E(K::Char('4'), M::CTRL_ALT),
565 (CTRL_ALT, 'u') => E(K::Char('5'), M::CTRL_ALT),
566 (CTRL_ALT, 'v') => E(K::Char('6'), M::CTRL_ALT),
567 (CTRL_ALT, 'w') => E(K::Char('7'), M::CTRL_ALT),
568 (CTRL_ALT, 'x') => E(K::Char('8'), M::CTRL_ALT),
569 (CTRL_ALT, 'y') => E(K::Char('9'), M::CTRL_ALT),
570 (CTRL_ALT_SHIFT, UP) => E(K::Up, M::CTRL_ALT_SHIFT),
571 (CTRL_ALT_SHIFT, DOWN) => E(K::Down, M::CTRL_ALT_SHIFT),
572 (CTRL_ALT_SHIFT, RIGHT) => E(K::Right, M::CTRL_ALT_SHIFT),
573 (CTRL_ALT_SHIFT, LEFT) => E(K::Left, M::CTRL_ALT_SHIFT),
574 (CTRL_ALT_SHIFT, END) => E(K::End, M::CTRL_ALT_SHIFT),
575 (CTRL_ALT_SHIFT, HOME) => E(K::Home, M::CTRL_ALT_SHIFT),
576 (CTRL_ALT_SHIFT, 'p') => E(K::Char('0'), M::CTRL_ALT_SHIFT),
577 (CTRL_ALT_SHIFT, 'q') => E(K::Char('1'), M::CTRL_ALT_SHIFT),
578 (CTRL_ALT_SHIFT, 'r') => E(K::Char('2'), M::CTRL_ALT_SHIFT),
579 (CTRL_ALT_SHIFT, 's') => E(K::Char('3'), M::CTRL_ALT_SHIFT),
580 (CTRL_ALT_SHIFT, 't') => E(K::Char('4'), M::CTRL_ALT_SHIFT),
581 (CTRL_ALT_SHIFT, 'u') => E(K::Char('5'), M::CTRL_ALT_SHIFT),
582 (CTRL_ALT_SHIFT, 'v') => E(K::Char('6'), M::CTRL_ALT_SHIFT),
583 (CTRL_ALT_SHIFT, 'w') => E(K::Char('7'), M::CTRL_ALT_SHIFT),
584 (CTRL_ALT_SHIFT, 'x') => E(K::Char('8'), M::CTRL_ALT_SHIFT),
585 (CTRL_ALT_SHIFT, 'y') => E(K::Char('9'), M::CTRL_ALT_SHIFT),
586 ('9', UP) => E(K::Up, M::ALT),
588 ('9', DOWN) => E(K::Down, M::ALT),
589 ('9', RIGHT) => E(K::Right, M::ALT),
590 ('9', LEFT) => E(K::Left, M::ALT),
591 _ => {
592 debug!(target: "rustyline",
593 "unsupported esc sequence: \\E[1;{}{:?}", seq4, seq5);
594 E(K::UnknownEscSeq, M::NONE)
595 }
596 })
597 } else if seq5 == '~' {
598 Ok(match (seq2, seq4) {
599 (INSERT, SHIFT) => E(K::Insert, M::SHIFT),
600 (INSERT, ALT) => E(K::Insert, M::ALT),
601 (INSERT, ALT_SHIFT) => E(K::Insert, M::ALT_SHIFT),
602 (INSERT, CTRL) => E(K::Insert, M::CTRL),
603 (INSERT, CTRL_SHIFT) => E(K::Insert, M::CTRL_SHIFT),
604 (INSERT, CTRL_ALT) => E(K::Insert, M::CTRL_ALT),
605 (INSERT, CTRL_ALT_SHIFT) => E(K::Insert, M::CTRL_ALT_SHIFT),
606 (DELETE, SHIFT) => E(K::Delete, M::SHIFT),
607 (DELETE, ALT) => E(K::Delete, M::ALT),
608 (DELETE, ALT_SHIFT) => E(K::Delete, M::ALT_SHIFT),
609 (DELETE, CTRL) => E(K::Delete, M::CTRL),
610 (DELETE, CTRL_SHIFT) => E(K::Delete, M::CTRL_SHIFT),
611 (DELETE, CTRL_ALT) => E(K::Delete, M::CTRL_ALT),
612 (DELETE, CTRL_ALT_SHIFT) => E(K::Delete, M::CTRL_ALT_SHIFT),
613 (PAGE_UP, SHIFT) => E(K::PageUp, M::SHIFT),
614 (PAGE_UP, ALT) => E(K::PageUp, M::ALT),
615 (PAGE_UP, ALT_SHIFT) => E(K::PageUp, M::ALT_SHIFT),
616 (PAGE_UP, CTRL) => E(K::PageUp, M::CTRL),
617 (PAGE_UP, CTRL_SHIFT) => E(K::PageUp, M::CTRL_SHIFT),
618 (PAGE_UP, CTRL_ALT) => E(K::PageUp, M::CTRL_ALT),
619 (PAGE_UP, CTRL_ALT_SHIFT) => E(K::PageUp, M::CTRL_ALT_SHIFT),
620 (PAGE_DOWN, SHIFT) => E(K::PageDown, M::SHIFT),
621 (PAGE_DOWN, ALT) => E(K::PageDown, M::ALT),
622 (PAGE_DOWN, ALT_SHIFT) => E(K::PageDown, M::ALT_SHIFT),
623 (PAGE_DOWN, CTRL) => E(K::PageDown, M::CTRL),
624 (PAGE_DOWN, CTRL_SHIFT) => E(K::PageDown, M::CTRL_SHIFT),
625 (PAGE_DOWN, CTRL_ALT) => E(K::PageDown, M::CTRL_ALT),
626 (PAGE_DOWN, CTRL_ALT_SHIFT) => E(K::PageDown, M::CTRL_ALT_SHIFT),
627 _ => {
628 debug!(target: "rustyline",
629 "unsupported esc sequence: \\E[{};{:?}~", seq2, seq4);
630 E(K::UnknownEscSeq, M::NONE)
631 }
632 })
633 } else {
634 debug!(target: "rustyline",
635 "unsupported esc sequence: \\E[{};{}{:?}", seq2, seq4, seq5);
636 Ok(E(K::UnknownEscSeq, M::NONE))
637 }
638 } else {
639 debug!(target: "rustyline",
640 "unsupported esc sequence: \\E[{};{:?}", seq2, seq4);
641 Ok(E(K::UnknownEscSeq, M::NONE))
642 }
643 } else {
644 Ok(match (seq2, seq3) {
645 (DELETE, RXVT_CTRL) => E(K::Delete, M::CTRL),
646 (DELETE, RXVT_CTRL_SHIFT) => E(K::Delete, M::CTRL_SHIFT),
647 (CTRL, UP) => E(K::Up, M::CTRL),
648 (CTRL, DOWN) => E(K::Down, M::CTRL),
649 (CTRL, RIGHT) => E(K::Right, M::CTRL),
650 (CTRL, LEFT) => E(K::Left, M::CTRL),
651 (PAGE_UP, RXVT_CTRL) => E(K::PageUp, M::CTRL),
652 (PAGE_UP, RXVT_SHIFT) => E(K::PageUp, M::SHIFT),
653 (PAGE_UP, RXVT_CTRL_SHIFT) => E(K::PageUp, M::CTRL_SHIFT),
654 (PAGE_DOWN, RXVT_CTRL) => E(K::PageDown, M::CTRL),
655 (PAGE_DOWN, RXVT_SHIFT) => E(K::PageDown, M::SHIFT),
656 (PAGE_DOWN, RXVT_CTRL_SHIFT) => E(K::PageDown, M::CTRL_SHIFT),
657 (RXVT_HOME, RXVT_CTRL) => E(K::Home, M::CTRL),
658 (RXVT_HOME, RXVT_SHIFT) => E(K::Home, M::SHIFT),
659 (RXVT_HOME, RXVT_CTRL_SHIFT) => E(K::Home, M::CTRL_SHIFT),
660 (RXVT_END, RXVT_CTRL) => E(K::End, M::CTRL), (RXVT_END, RXVT_SHIFT) => E(K::End, M::SHIFT),
662 (RXVT_END, RXVT_CTRL_SHIFT) => E(K::End, M::CTRL_SHIFT),
663 _ => {
664 debug!(target: "rustyline",
665 "unsupported esc sequence: \\E[{}{:?}", seq2, seq3);
666 E(K::UnknownEscSeq, M::NONE)
667 }
668 })
669 }
670 }
671
672 fn escape_o(&mut self) -> Result<KeyEvent> {
674 let seq2 = self.next_char()?;
675 Ok(match seq2 {
676 UP => E(K::Up, M::NONE),
677 DOWN => E(K::Down, M::NONE),
678 RIGHT => E(K::Right, M::NONE),
679 LEFT => E(K::Left, M::NONE),
680 END => E(K::End, M::NONE), HOME => E(K::Home, M::NONE), 'M' => E::ENTER, 'P' => E(K::F(1), M::NONE), 'Q' => E(K::F(2), M::NONE), 'R' => E(K::F(3), M::NONE), 'S' => E(K::F(4), M::NONE), 'a' => E(K::Up, M::CTRL),
689 'b' => E(K::Down, M::CTRL),
690 'c' => E(K::Right, M::CTRL), 'd' => E(K::Left, M::CTRL), 'l' => E(K::F(8), M::NONE),
693 't' => E(K::F(5), M::NONE), 'u' => E(K::F(6), M::NONE), 'v' => E(K::F(7), M::NONE), 'w' => E(K::F(9), M::NONE), 'x' => E(K::F(10), M::NONE), _ => {
699 debug!(target: "rustyline", "unsupported esc sequence: \\EO{:?}", seq2);
700 E(K::UnknownEscSeq, M::NONE)
701 }
702 })
703 }
704
705 fn poll(&mut self, timeout_ms: PollTimeout) -> Result<i32> {
706 let n = self.tty_in.buffer().len();
707 if n > 0 {
708 return Ok(n as i32);
709 }
710 let mut fds = [poll::PollFd::new(self.as_fd(), PollFlags::POLLIN)];
711 let r = poll::poll(&mut fds, timeout_ms);
712 match r {
713 Ok(n) => Ok(n),
714 Err(Errno::EINTR) => {
715 if self.tty_in.get_ref().sigwinch()? {
716 Err(ReadlineError::WindowResized)
717 } else {
718 Ok(0) }
720 }
721 Err(e) => Err(e.into()),
722 }
723 }
724
725 fn select(&mut self, single_esc_abort: bool) -> Result<Event> {
726 let tty_in = self.as_fd();
727 let sigwinch_pipe = self
728 .tty_in
729 .get_ref()
730 .sigwinch_pipe
731 .map(|fd| unsafe { BorrowedFd::borrow_raw(fd) });
732 let pipe_reader = self
733 .pipe_reader
734 .as_ref()
735 .map(|pr| pr.lock().unwrap().0.as_raw_fd())
736 .map(|fd| unsafe { BorrowedFd::borrow_raw(fd) });
737 loop {
738 let mut readfds = FdSet::new();
739 if let Some(sigwinch_pipe) = sigwinch_pipe {
740 readfds.insert(sigwinch_pipe);
741 }
742 readfds.insert(tty_in);
743 if let Some(pipe_reader) = pipe_reader {
744 readfds.insert(pipe_reader);
745 }
746 if let Err(err) = select::select(None, Some(&mut readfds), None, None, None) {
747 if err == Errno::EINTR && self.tty_in.get_ref().sigwinch()? {
748 return Err(ReadlineError::WindowResized);
749 } else if err != Errno::EINTR {
750 return Err(err.into());
751 } else {
752 continue;
753 }
754 };
755 if sigwinch_pipe.map_or(false, |fd| readfds.contains(fd)) {
756 self.tty_in.get_ref().sigwinch()?;
757 return Err(ReadlineError::WindowResized);
758 } else if readfds.contains(tty_in) {
759 return self.next_key(single_esc_abort).map(Event::KeyPress);
761 } else if let Some(ref pipe_reader) = self.pipe_reader {
762 let mut guard = pipe_reader.lock().unwrap();
763 let mut buf = [0; 1];
764 guard.0.read_exact(&mut buf)?;
765 if let Ok(msg) = guard.1.try_recv() {
766 return Ok(Event::ExternalPrint(msg));
767 }
768 }
769 }
770 }
771}
772
773impl RawReader for PosixRawReader {
774 type Buffer = PosixBuffer;
775
776 #[cfg(not(feature = "signal-hook"))]
777 fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
778 match self.pipe_reader {
779 Some(_) => self.select(single_esc_abort),
780 None => self.next_key(single_esc_abort).map(Event::KeyPress),
781 }
782 }
783
784 #[cfg(feature = "signal-hook")]
785 fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
786 self.select(single_esc_abort)
787 }
788
789 fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyEvent> {
790 let c = self.next_char()?;
791
792 let mut key = KeyEvent::new(c, M::NONE);
793 if key == E::ESC {
794 if !self.tty_in.buffer().is_empty() {
795 debug!(target: "rustyline", "read buffer {:?}", self.tty_in.buffer());
796 }
797 let timeout_ms = if single_esc_abort && self.timeout_ms.is_none() {
798 PollTimeout::ZERO
799 } else {
800 self.timeout_ms
801 };
802 match self.poll(timeout_ms) {
803 Ok(0) => {
804 }
806 Ok(_) => {
807 key = self.escape_sequence()?
809 }
810 Err(e) => return Err(e),
812 }
813 }
814 debug!(target: "rustyline", "c: {:?} => key: {:?}", c, key);
815 Ok(key)
816 }
817
818 fn next_char(&mut self) -> Result<char> {
819 let mut buf = [0; 1];
820 let mut receiver = Utf8 {
821 c: None,
822 valid: true,
823 };
824 loop {
825 let n = self.tty_in.read(&mut buf)?;
826 if n == 0 {
827 return Err(ReadlineError::Eof);
828 }
829 let b = buf[0];
830 self.parser.advance(&mut receiver, b);
831 if !receiver.valid {
832 return Err(ReadlineError::from(ErrorKind::InvalidData));
833 } else if let Some(c) = receiver.c.take() {
834 return Ok(c);
835 }
836 }
837 }
838
839 fn read_pasted_text(&mut self) -> Result<String> {
840 let mut buffer = String::new();
841 loop {
842 match self.next_char()? {
843 '\x1b' => {
844 let key = self.escape_sequence()?;
845 if key == E(K::BracketedPasteEnd, M::NONE) {
846 break;
847 } else {
848 continue; }
850 }
851 c => buffer.push(c),
852 };
853 }
854 let buffer = buffer.replace("\r\n", "\n");
855 let buffer = buffer.replace('\r', "\n");
856 Ok(buffer)
857 }
858
859 fn find_binding(&self, key: &KeyEvent) -> Option<Cmd> {
860 let cmd = self.key_map.get(key).cloned();
861 if let Some(ref cmd) = cmd {
862 debug!(target: "rustyline", "terminal key binding: {:?} => {:?}", key, cmd);
863 }
864 cmd
865 }
866
867 #[cfg(any(not(feature = "buffer-redux"), test))]
868 fn unbuffer(self) -> Option<PosixBuffer> {
869 None
870 }
871
872 #[cfg(all(feature = "buffer-redux", not(test)))]
873 fn unbuffer(self) -> Option<PosixBuffer> {
874 let (_, buffer) = self.tty_in.into_inner_with_buffer();
875 Some(buffer)
876 }
877}
878
879impl Receiver for Utf8 {
880 fn codepoint(&mut self, c: char) {
882 self.c = Some(c);
883 self.valid = true;
884 }
885
886 fn invalid_sequence(&mut self) {
888 self.c = None;
889 self.valid = false;
890 }
891}
892
893pub struct PosixRenderer {
895 out: RawFd,
896 cols: usize, buffer: String,
898 tab_stop: usize,
899 colors_enabled: bool,
900 bell_style: BellStyle,
901}
902
903impl PosixRenderer {
904 fn new(out: RawFd, tab_stop: usize, colors_enabled: bool, bell_style: BellStyle) -> Self {
905 let (cols, _) = get_win_size(out);
906 Self {
907 out,
908 cols,
909 buffer: String::with_capacity(1024),
910 tab_stop,
911 colors_enabled,
912 bell_style,
913 }
914 }
915
916 fn clear_old_rows(&mut self, layout: &Layout) {
917 use std::fmt::Write;
918 let current_row = layout.cursor.row;
919 let old_rows = layout.end.row;
920 let cursor_row_movement = old_rows.saturating_sub(current_row);
923 if cursor_row_movement > 0 {
925 write!(self.buffer, "\x1b[{cursor_row_movement}B").unwrap();
926 }
927 for _ in 0..old_rows {
929 self.buffer.push_str("\r\x1b[K\x1b[A");
930 }
931 self.buffer.push_str("\r\x1b[K");
933 }
934}
935
936impl Renderer for PosixRenderer {
937 type Reader = PosixRawReader;
938
939 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
940 use std::fmt::Write;
941 self.buffer.clear();
942 let row_ordering = new.row.cmp(&old.row);
943 if row_ordering == cmp::Ordering::Greater {
944 let row_shift = new.row - old.row;
946 if row_shift == 1 {
947 self.buffer.push_str("\x1b[B");
948 } else {
949 write!(self.buffer, "\x1b[{row_shift}B")?;
950 }
951 } else if row_ordering == cmp::Ordering::Less {
952 let row_shift = old.row - new.row;
954 if row_shift == 1 {
955 self.buffer.push_str("\x1b[A");
956 } else {
957 write!(self.buffer, "\x1b[{row_shift}A")?;
958 }
959 }
960 let col_ordering = new.col.cmp(&old.col);
961 if col_ordering == cmp::Ordering::Greater {
962 let col_shift = new.col - old.col;
964 if col_shift == 1 {
965 self.buffer.push_str("\x1b[C");
966 } else {
967 write!(self.buffer, "\x1b[{col_shift}C")?;
968 }
969 } else if col_ordering == cmp::Ordering::Less {
970 let col_shift = old.col - new.col;
972 if col_shift == 1 {
973 self.buffer.push_str("\x1b[D");
974 } else {
975 write!(self.buffer, "\x1b[{col_shift}D")?;
976 }
977 }
978 write_all(self.out, self.buffer.as_str())?;
979 Ok(())
980 }
981
982 fn refresh_line(
983 &mut self,
984 prompt: &str,
985 line: &LineBuffer,
986 hint: Option<&str>,
987 old_layout: &Layout,
988 new_layout: &Layout,
989 highlighter: Option<&dyn Highlighter>,
990 ) -> Result<()> {
991 use std::fmt::Write;
992 self.buffer.clear();
993
994 let default_prompt = new_layout.default_prompt;
995 let cursor = new_layout.cursor;
996 let end_pos = new_layout.end;
997
998 self.clear_old_rows(old_layout);
999
1000 if let Some(highlighter) = highlighter {
1001 self.buffer
1003 .push_str(&highlighter.highlight_prompt(prompt, default_prompt));
1004 self.buffer
1006 .push_str(&highlighter.highlight(line, line.pos()));
1007 } else {
1008 self.buffer.push_str(prompt);
1010 self.buffer.push_str(line);
1012 }
1013 if let Some(hint) = hint {
1015 if let Some(highlighter) = highlighter {
1016 self.buffer.push_str(&highlighter.highlight_hint(hint));
1017 } else {
1018 self.buffer.push_str(hint);
1019 }
1020 }
1021 if end_pos.col == 0
1023 && end_pos.row > 0
1024 && !hint
1025 .map(|h| h.ends_with('\n'))
1026 .unwrap_or_else(|| line.ends_with('\n'))
1027 {
1028 self.buffer.push('\n');
1029 }
1030 let new_cursor_row_movement = end_pos.row - cursor.row;
1032 if new_cursor_row_movement > 0 {
1034 write!(self.buffer, "\x1b[{new_cursor_row_movement}A")?;
1035 }
1036 if cursor.col > 0 {
1038 write!(self.buffer, "\r\x1b[{}C", cursor.col).unwrap();
1039 } else {
1040 self.buffer.push('\r');
1041 }
1042
1043 write_all(self.out, self.buffer.as_str())?;
1044 Ok(())
1045 }
1046
1047 fn write_and_flush(&mut self, buf: &str) -> Result<()> {
1048 write_all(self.out, buf)?;
1049 Ok(())
1050 }
1051
1052 fn calculate_position(&self, s: &str, orig: Position) -> Position {
1055 let mut pos = orig;
1056 let mut esc_seq = 0;
1057 for c in s.graphemes(true) {
1058 if c == "\n" {
1059 pos.row += 1;
1060 pos.col = 0;
1061 continue;
1062 }
1063 let cw = if c == "\t" {
1064 self.tab_stop - (pos.col % self.tab_stop)
1065 } else {
1066 width(c, &mut esc_seq)
1067 };
1068 pos.col += cw;
1069 if pos.col > self.cols {
1070 pos.row += 1;
1071 pos.col = cw;
1072 }
1073 }
1074 if pos.col == self.cols {
1075 pos.col = 0;
1076 pos.row += 1;
1077 }
1078 pos
1079 }
1080
1081 fn beep(&mut self) -> Result<()> {
1082 match self.bell_style {
1083 BellStyle::Audible => self.write_and_flush("\x07"),
1084 _ => Ok(()),
1085 }
1086 }
1087
1088 fn clear_screen(&mut self) -> Result<()> {
1090 self.write_and_flush("\x1b[H\x1b[J")
1091 }
1092
1093 fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
1094 self.buffer.clear();
1095 self.clear_old_rows(layout);
1096 write_all(self.out, self.buffer.as_str())?;
1097 Ok(())
1098 }
1099
1100 fn update_size(&mut self) {
1102 let (cols, _) = get_win_size(self.out);
1103 self.cols = cols;
1104 }
1105
1106 fn get_columns(&self) -> usize {
1107 self.cols
1108 }
1109
1110 fn get_rows(&self) -> usize {
1113 let (_, rows) = get_win_size(self.out);
1114 rows
1115 }
1116
1117 fn colors_enabled(&self) -> bool {
1118 self.colors_enabled
1119 }
1120
1121 fn move_cursor_at_leftmost(&mut self, rdr: &mut PosixRawReader) -> Result<()> {
1122 if rdr.poll(PollTimeout::ZERO)? != 0 {
1123 debug!(target: "rustyline", "cannot request cursor location");
1124 return Ok(());
1125 }
1126 self.write_and_flush("\x1b[6n")?;
1128 if rdr.poll(PollTimeout::from(100u8))? == 0
1130 || rdr.next_char()? != '\x1b'
1131 || rdr.next_char()? != '['
1132 || read_digits_until(rdr, ';')?.is_none()
1133 {
1134 warn!(target: "rustyline", "cannot read initial cursor location");
1135 return Ok(());
1136 }
1137 let col = read_digits_until(rdr, 'R')?;
1138 debug!(target: "rustyline", "initial cursor location: {:?}", col);
1139 if col != Some(1) {
1140 self.write_and_flush("\n")?;
1141 }
1142 Ok(())
1143 }
1144}
1145
1146fn read_digits_until(rdr: &mut PosixRawReader, sep: char) -> Result<Option<u32>> {
1147 let mut num: u32 = 0;
1148 loop {
1149 match rdr.next_char()? {
1150 digit @ '0'..='9' => {
1151 num = num
1152 .saturating_mul(10)
1153 .saturating_add(digit.to_digit(10).unwrap());
1154 continue;
1155 }
1156 c if c == sep => break,
1157 _ => return Ok(None),
1158 }
1159 }
1160 Ok(Some(num))
1161}
1162
1163fn write_all(fd: RawFd, buf: &str) -> nix::Result<()> {
1164 let mut bytes = buf.as_bytes();
1165 while !bytes.is_empty() {
1166 match write(unsafe { BorrowedFd::borrow_raw(fd) }, bytes) {
1167 Ok(0) => return Err(Errno::EIO),
1168 Ok(n) => bytes = &bytes[n..],
1169 Err(Errno::EINTR) => {}
1170 Err(r) => return Err(r),
1171 }
1172 }
1173 Ok(())
1174}
1175
1176pub struct PosixCursorGuard(RawFd);
1177
1178impl Drop for PosixCursorGuard {
1179 fn drop(&mut self) {
1180 let _ = set_cursor_visibility(self.0, true);
1181 }
1182}
1183
1184fn set_cursor_visibility(fd: RawFd, visible: bool) -> Result<Option<PosixCursorGuard>> {
1185 write_all(fd, if visible { "\x1b[?25h" } else { "\x1b[?25l" })?;
1186 Ok(if visible {
1187 None
1188 } else {
1189 Some(PosixCursorGuard(fd))
1190 })
1191}
1192
1193#[cfg(not(feature = "signal-hook"))]
1194static mut SIGWINCH_PIPE: RawFd = -1;
1195#[cfg(not(feature = "signal-hook"))]
1196extern "C" fn sigwinch_handler(_: libc::c_int) {
1197 let _ = unsafe { write(BorrowedFd::borrow_raw(SIGWINCH_PIPE), &[b's']) };
1198}
1199
1200#[derive(Clone, Debug)]
1201struct SigWinCh {
1202 pipe: RawFd,
1203 #[cfg(not(feature = "signal-hook"))]
1204 original: nix::sys::signal::SigAction,
1205 #[cfg(feature = "signal-hook")]
1206 id: signal_hook::SigId,
1207}
1208impl SigWinCh {
1209 #[cfg(not(feature = "signal-hook"))]
1210 fn install_sigwinch_handler() -> Result<SigWinCh> {
1211 use nix::sys::signal;
1212 let (pipe, pipe_write) = UnixStream::pair()?;
1213 pipe.set_nonblocking(true)?;
1214 unsafe { SIGWINCH_PIPE = pipe_write.into_raw_fd() };
1215 let sigwinch = signal::SigAction::new(
1216 signal::SigHandler::Handler(sigwinch_handler),
1217 signal::SaFlags::empty(),
1218 signal::SigSet::empty(),
1219 );
1220 let original = unsafe { signal::sigaction(signal::SIGWINCH, &sigwinch)? };
1221 Ok(SigWinCh {
1222 pipe: pipe.into_raw_fd(),
1223 original,
1224 })
1225 }
1226
1227 #[cfg(feature = "signal-hook")]
1228 fn install_sigwinch_handler() -> Result<SigWinCh> {
1229 let (pipe, pipe_write) = UnixStream::pair()?;
1230 pipe.set_nonblocking(true)?;
1231 let id = signal_hook::low_level::pipe::register(libc::SIGWINCH, pipe_write)?;
1232 Ok(SigWinCh {
1233 pipe: pipe.into_raw_fd(),
1234 id,
1235 })
1236 }
1237
1238 #[cfg(not(feature = "signal-hook"))]
1239 fn uninstall_sigwinch_handler(self) -> Result<()> {
1240 use nix::sys::signal;
1241 let _ = unsafe { signal::sigaction(signal::SIGWINCH, &self.original)? };
1242 close(self.pipe)?;
1243 unsafe { close(SIGWINCH_PIPE)? };
1244 unsafe { SIGWINCH_PIPE = -1 };
1245 Ok(())
1246 }
1247
1248 #[cfg(feature = "signal-hook")]
1249 fn uninstall_sigwinch_handler(self) -> Result<()> {
1250 signal_hook::low_level::unregister(self.id);
1251 close(self.pipe)?;
1252 Ok(())
1253 }
1254}
1255
1256#[cfg(not(test))]
1257pub type Terminal = PosixTerminal;
1258
1259#[derive(Clone, Debug)]
1260pub struct PosixTerminal {
1261 unsupported: bool,
1262 tty_in: RawFd,
1263 is_in_a_tty: bool,
1264 tty_out: RawFd,
1265 is_out_a_tty: bool,
1266 close_on_drop: bool,
1267 pub(crate) color_mode: ColorMode,
1268 tab_stop: usize,
1269 bell_style: BellStyle,
1270 enable_bracketed_paste: bool,
1271 raw_mode: Arc<AtomicBool>,
1272 pipe_reader: Option<PipeReader>,
1274 pipe_writer: Option<PipeWriter>,
1276 sigwinch: Option<SigWinCh>,
1277 enable_signals: bool,
1278}
1279
1280impl PosixTerminal {
1281 fn colors_enabled(&self) -> bool {
1282 match self.color_mode {
1283 ColorMode::Enabled => self.is_out_a_tty,
1284 ColorMode::Forced => true,
1285 ColorMode::Disabled => false,
1286 }
1287 }
1288}
1289
1290impl Term for PosixTerminal {
1291 type Buffer = PosixBuffer;
1292 type CursorGuard = PosixCursorGuard;
1293 type ExternalPrinter = ExternalPrinter;
1294 type KeyMap = PosixKeyMap;
1295 type Mode = PosixMode;
1296 type Reader = PosixRawReader;
1297 type Writer = PosixRenderer;
1298
1299 fn new(
1300 color_mode: ColorMode,
1301 behavior: Behavior,
1302 tab_stop: usize,
1303 bell_style: BellStyle,
1304 enable_bracketed_paste: bool,
1305 enable_signals: bool,
1306 ) -> Result<Self> {
1307 let (tty_in, is_in_a_tty, tty_out, is_out_a_tty, close_on_drop) =
1308 if behavior == Behavior::PreferTerm {
1309 let tty = OpenOptions::new().read(true).write(true).open("/dev/tty");
1310 if let Ok(tty) = tty {
1311 let fd = tty.into_raw_fd();
1312 let is_a_tty = is_a_tty(fd); (fd, is_a_tty, fd, is_a_tty, true)
1314 } else {
1315 (
1316 libc::STDIN_FILENO,
1317 is_a_tty(libc::STDIN_FILENO),
1318 libc::STDOUT_FILENO,
1319 is_a_tty(libc::STDOUT_FILENO),
1320 false,
1321 )
1322 }
1323 } else {
1324 (
1325 libc::STDIN_FILENO,
1326 is_a_tty(libc::STDIN_FILENO),
1327 libc::STDOUT_FILENO,
1328 is_a_tty(libc::STDOUT_FILENO),
1329 false,
1330 )
1331 };
1332 let unsupported = is_unsupported_term();
1333 #[allow(unused_variables)]
1334 let sigwinch = if !unsupported && is_in_a_tty && is_out_a_tty {
1335 Some(SigWinCh::install_sigwinch_handler()?)
1336 } else {
1337 None
1338 };
1339 Ok(Self {
1340 unsupported,
1341 tty_in,
1342 is_in_a_tty,
1343 tty_out,
1344 is_out_a_tty,
1345 close_on_drop,
1346 color_mode,
1347 tab_stop,
1348 bell_style,
1349 enable_bracketed_paste,
1350 raw_mode: Arc::new(AtomicBool::new(false)),
1351 pipe_reader: None,
1352 pipe_writer: None,
1353 sigwinch,
1354 enable_signals,
1355 })
1356 }
1357
1358 fn is_unsupported(&self) -> bool {
1363 self.unsupported
1364 }
1365
1366 fn is_input_tty(&self) -> bool {
1367 self.is_in_a_tty
1368 }
1369
1370 fn is_output_tty(&self) -> bool {
1371 self.is_out_a_tty
1372 }
1373
1374 fn enable_raw_mode(&mut self) -> Result<(Self::Mode, PosixKeyMap)> {
1377 use nix::errno::Errno::ENOTTY;
1378 if !self.is_in_a_tty {
1379 return Err(ENOTTY.into());
1380 }
1381 let (original_mode, key_map) = termios_::enable_raw_mode(self.tty_in, self.enable_signals)?;
1382
1383 self.raw_mode.store(true, Ordering::SeqCst);
1384 let out = if !self.enable_bracketed_paste {
1386 None
1387 } else if let Err(e) = write_all(self.tty_out, BRACKETED_PASTE_ON) {
1388 debug!(target: "rustyline", "Cannot enable bracketed paste: {}", e);
1389 None
1390 } else {
1391 Some(self.tty_out)
1392 };
1393
1394 if Arc::strong_count(&self.raw_mode) == 1 {
1396 self.pipe_writer = None;
1397 self.pipe_reader = None;
1398 }
1399
1400 Ok((
1401 PosixMode {
1402 termios: original_mode,
1403 tty_in: self.tty_in,
1404 tty_out: out,
1405 raw_mode: self.raw_mode.clone(),
1406 },
1407 key_map,
1408 ))
1409 }
1410
1411 fn create_reader(
1413 &self,
1414 buffer: Option<PosixBuffer>,
1415 config: &Config,
1416 key_map: PosixKeyMap,
1417 ) -> PosixRawReader {
1418 PosixRawReader::new(
1419 self.tty_in,
1420 self.sigwinch.as_ref().map(|s| s.pipe),
1421 buffer,
1422 config,
1423 key_map,
1424 self.pipe_reader.clone(),
1425 )
1426 }
1427
1428 fn create_writer(&self) -> PosixRenderer {
1429 PosixRenderer::new(
1430 self.tty_out,
1431 self.tab_stop,
1432 self.colors_enabled(),
1433 self.bell_style,
1434 )
1435 }
1436
1437 fn writeln(&self) -> Result<()> {
1438 write_all(self.tty_out, "\n")?;
1439 Ok(())
1440 }
1441
1442 fn create_external_printer(&mut self) -> Result<ExternalPrinter> {
1443 if let Some(ref writer) = self.pipe_writer {
1444 return Ok(ExternalPrinter {
1445 writer: writer.clone(),
1446 raw_mode: self.raw_mode.clone(),
1447 tty_out: self.tty_out,
1448 });
1449 }
1450 if self.unsupported || !self.is_input_tty() || !self.is_output_tty() {
1451 return Err(nix::Error::ENOTTY.into());
1452 }
1453 use nix::unistd::pipe;
1454 let (sender, receiver) = mpsc::sync_channel(1); let (r, w) = pipe()?;
1456 let reader = Arc::new(Mutex::new((r.into(), receiver)));
1457 let writer = (Arc::new(Mutex::new(w.into())), sender);
1458 self.pipe_reader.replace(reader);
1459 self.pipe_writer.replace(writer.clone());
1460 Ok(ExternalPrinter {
1461 writer,
1462 raw_mode: self.raw_mode.clone(),
1463 tty_out: self.tty_out,
1464 })
1465 }
1466
1467 fn set_cursor_visibility(&mut self, visible: bool) -> Result<Option<PosixCursorGuard>> {
1468 if self.is_out_a_tty {
1469 set_cursor_visibility(self.tty_out, visible)
1470 } else {
1471 Ok(None)
1472 }
1473 }
1474}
1475
1476#[allow(unused_must_use)]
1477impl Drop for PosixTerminal {
1478 fn drop(&mut self) {
1479 if self.close_on_drop {
1480 close(self.tty_in);
1481 debug_assert_eq!(self.tty_in, self.tty_out);
1482 }
1483 if let Some(sigwinch) = self.sigwinch.take() {
1484 sigwinch.uninstall_sigwinch_handler();
1485 }
1486 }
1487}
1488
1489#[derive(Debug)]
1490pub struct ExternalPrinter {
1491 writer: PipeWriter,
1492 raw_mode: Arc<AtomicBool>,
1493 tty_out: RawFd,
1494}
1495
1496impl super::ExternalPrinter for ExternalPrinter {
1497 fn print(&mut self, msg: String) -> Result<()> {
1498 if !self.raw_mode.load(Ordering::SeqCst) {
1500 write_all(self.tty_out, msg.as_str())?;
1501 } else if let Ok(mut writer) = self.writer.0.lock() {
1502 self.writer
1503 .1
1504 .send(msg)
1505 .map_err(|_| io::Error::from(ErrorKind::Other))?; writer.write_all(&[b'm'])?;
1507 writer.flush()?;
1508 } else {
1509 return Err(io::Error::from(ErrorKind::Other).into()); }
1511 Ok(())
1512 }
1513}
1514
1515#[cfg(not(test))]
1516pub fn suspend() -> Result<()> {
1517 use nix::sys::signal;
1518 use nix::unistd::Pid;
1519 signal::kill(Pid::from_raw(0), signal::SIGTSTP)?;
1521 Ok(())
1522}
1523
1524#[cfg(not(feature = "termios"))]
1525mod termios_ {
1526 use super::PosixKeyMap;
1527 use crate::keys::{KeyEvent, Modifiers as M};
1528 use crate::{Cmd, Result};
1529 use nix::sys::termios::{self, SetArg, SpecialCharacterIndices as SCI, Termios};
1530 use std::collections::HashMap;
1531 use std::os::unix::io::{BorrowedFd, RawFd};
1532 pub fn disable_raw_mode(tty_in: RawFd, termios: &Termios) -> Result<()> {
1533 let fd = unsafe { BorrowedFd::borrow_raw(tty_in) };
1534 Ok(termios::tcsetattr(fd, SetArg::TCSADRAIN, termios)?)
1535 }
1536 pub fn enable_raw_mode(tty_in: RawFd, enable_signals: bool) -> Result<(Termios, PosixKeyMap)> {
1537 use nix::sys::termios::{ControlFlags, InputFlags, LocalFlags};
1538
1539 let fd = unsafe { BorrowedFd::borrow_raw(tty_in) };
1540 let original_mode = termios::tcgetattr(fd)?;
1541 let mut raw = original_mode.clone();
1542 raw.input_flags &= !(InputFlags::BRKINT
1545 | InputFlags::ICRNL
1546 | InputFlags::INPCK
1547 | InputFlags::ISTRIP
1548 | InputFlags::IXON);
1549 raw.control_flags |= ControlFlags::CS8;
1555 raw.local_flags &=
1557 !(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG);
1558
1559 if enable_signals {
1560 raw.local_flags |= LocalFlags::ISIG;
1561 }
1562
1563 raw.control_chars[SCI::VMIN as usize] = 1; raw.control_chars[SCI::VTIME as usize] = 0; let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
1567 map_key(&mut key_map, &raw, SCI::VEOF, "VEOF", Cmd::EndOfFile);
1568 map_key(&mut key_map, &raw, SCI::VINTR, "VINTR", Cmd::Interrupt);
1569 map_key(&mut key_map, &raw, SCI::VQUIT, "VQUIT", Cmd::Interrupt);
1570 map_key(&mut key_map, &raw, SCI::VSUSP, "VSUSP", Cmd::Suspend);
1571
1572 termios::tcsetattr(fd, SetArg::TCSADRAIN, &raw)?;
1573 Ok((original_mode, key_map))
1574 }
1575 fn map_key(
1576 key_map: &mut HashMap<KeyEvent, Cmd>,
1577 raw: &Termios,
1578 index: SCI,
1579 name: &str,
1580 cmd: Cmd,
1581 ) {
1582 let cc = char::from(raw.control_chars[index as usize]);
1583 let key = KeyEvent::new(cc, M::NONE);
1584 log::debug!(target: "rustyline", "{}: {:?}", name, key);
1585 key_map.insert(key, cmd);
1586 }
1587}
1588#[cfg(feature = "termios")]
1589mod termios_ {
1590 use super::PosixKeyMap;
1591 use crate::keys::{KeyEvent, Modifiers as M};
1592 use crate::{Cmd, Result};
1593 use std::collections::HashMap;
1594 use std::os::unix::io::RawFd;
1595 use termios::{self, Termios};
1596 pub fn disable_raw_mode(tty_in: RawFd, termios: &Termios) -> Result<()> {
1597 Ok(termios::tcsetattr(tty_in, termios::TCSADRAIN, termios)?)
1598 }
1599 pub fn enable_raw_mode(tty_in: RawFd, enable_signals: bool) -> Result<(Termios, PosixKeyMap)> {
1600 let original_mode = Termios::from_fd(tty_in)?;
1601 let mut raw = original_mode;
1602 raw.c_iflag &=
1605 !(termios::BRKINT | termios::ICRNL | termios::INPCK | termios::ISTRIP | termios::IXON);
1606 raw.c_cflag |= termios::CS8;
1612 raw.c_lflag &= !(termios::ECHO | termios::ICANON | termios::IEXTEN | termios::ISIG);
1614
1615 if enable_signals {
1616 raw.c_lflag |= termios::ISIG;
1617 }
1618
1619 raw.c_cc[termios::VMIN] = 1; raw.c_cc[termios::VTIME] = 0; let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
1623 map_key(&mut key_map, &raw, termios::VEOF, "VEOF", Cmd::EndOfFile);
1624 map_key(&mut key_map, &raw, termios::VINTR, "VINTR", Cmd::Interrupt);
1625 map_key(&mut key_map, &raw, termios::VQUIT, "VQUIT", Cmd::Interrupt);
1626 map_key(&mut key_map, &raw, termios::VSUSP, "VSUSP", Cmd::Suspend);
1627
1628 termios::tcsetattr(tty_in, termios::TCSADRAIN, &raw)?;
1629 Ok((original_mode, key_map))
1630 }
1631 fn map_key(
1632 key_map: &mut HashMap<KeyEvent, Cmd>,
1633 raw: &Termios,
1634 index: usize,
1635 name: &str,
1636 cmd: Cmd,
1637 ) {
1638 let cc = char::from(raw.c_cc[index]);
1639 let key = KeyEvent::new(cc, M::NONE);
1640 log::debug!(target: "rustyline", "{}: {:?}", name, key);
1641 key_map.insert(key, cmd);
1642 }
1643}
1644
1645#[cfg(test)]
1646mod test {
1647 use super::{Position, PosixRenderer, PosixTerminal, Renderer};
1648 use crate::config::BellStyle;
1649 use crate::line_buffer::{LineBuffer, NoListener};
1650
1651 #[test]
1652 #[ignore]
1653 fn prompt_with_ansi_escape_codes() {
1654 let out = PosixRenderer::new(libc::STDOUT_FILENO, 4, true, BellStyle::default());
1655 let pos = out.calculate_position("\x1b[1;32m>>\x1b[0m ", Position::default());
1656 assert_eq!(3, pos.col);
1657 assert_eq!(0, pos.row);
1658 }
1659
1660 #[test]
1661 fn test_unsupported_term() {
1662 std::env::set_var("TERM", "xterm");
1663 assert!(!super::is_unsupported_term());
1664
1665 std::env::set_var("TERM", "dumb");
1666 assert!(super::is_unsupported_term());
1667 }
1668
1669 #[test]
1670 fn test_send() {
1671 fn assert_send<T: Send>() {}
1672 assert_send::<PosixTerminal>();
1673 }
1674
1675 #[test]
1676 fn test_sync() {
1677 fn assert_sync<T: Sync>() {}
1678 assert_sync::<PosixTerminal>();
1679 }
1680
1681 #[test]
1682 fn test_line_wrap() {
1683 let mut out = PosixRenderer::new(libc::STDOUT_FILENO, 4, true, BellStyle::default());
1684 let prompt = "> ";
1685 let default_prompt = true;
1686 let prompt_size = out.calculate_position(prompt, Position::default());
1687
1688 let mut line = LineBuffer::init("", 0);
1689 let old_layout = out.compute_layout(prompt_size, default_prompt, &line, None);
1690 assert_eq!(Position { col: 2, row: 0 }, old_layout.cursor);
1691 assert_eq!(old_layout.cursor, old_layout.end);
1692
1693 assert_eq!(
1694 Some(true),
1695 line.insert('a', out.cols - prompt_size.col + 1, &mut NoListener)
1696 );
1697 let new_layout = out.compute_layout(prompt_size, default_prompt, &line, None);
1698 assert_eq!(Position { col: 1, row: 1 }, new_layout.cursor);
1699 assert_eq!(new_layout.cursor, new_layout.end);
1700 out.refresh_line(prompt, &line, None, &old_layout, &new_layout, None)
1701 .unwrap();
1702 #[rustfmt::skip]
1703 assert_eq!(
1704 "\r\u{1b}[K> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\u{1b}[1C",
1705 out.buffer
1706 );
1707 }
1708}