rustyline/tty/
unix.rs

1//! Unix specific definitions
2#[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
36/// Unsupported Terminals that don't support RAW mode
37const 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                // In linux pseudo-terminals are created with dimensions of
57                // zero. If host application didn't initialize the correct
58                // size before start we treat zero size as 80 columns and
59                // infinite rows
60                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
77/// Check TERM environment variable to see if current term is in our
78/// unsupported list
79fn 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
93/// Return whether or not STDIN, STDOUT or STDERR is a TTY
94fn 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    /// Disable RAW mode for the terminal.
122    fn disable_raw_mode(&self) -> Result<()> {
123        termios_::disable_raw_mode(self.tty_in, &self.termios)?;
124        // disable bracketed paste
125        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
133// Rust std::io::Stdin is buffered with no way to know if bytes are available.
134// So we use low-level stuff instead...
135struct 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    /// Check if a SIGWINCH signal has been received
170    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
185// (native receiver with a selectable file descriptor, actual message receiver)
186type PipeReader = Arc<Mutex<(File, mpsc::Receiver<String>)>>;
187// (native sender, actual message sender)
188type PipeWriter = (Arc<Mutex<File>>, SyncSender<String>);
189
190/// Console input reader
191pub struct PosixRawReader {
192    tty_in: BufReader<TtyIn>,
193    timeout_ms: PollTimeout,
194    parser: Parser,
195    key_map: PosixKeyMap,
196    // external print reader
197    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'; // kcuu1, kUP*
213const DOWN: char = 'B'; // kcud1, kDN*
214const RIGHT: char = 'C'; // kcuf1, kRIT*
215const LEFT: char = 'D'; // kcub1, kLFT*
216const END: char = 'F'; // kend*
217const HOME: char = 'H'; // khom*
218const INSERT: char = '2'; // kic*
219const DELETE: char = '3'; // kdch1, kDC*
220const PAGE_UP: char = '5'; // kpp, kPRV*
221const PAGE_DOWN: char = '6'; // knp, kNXT*
222
223const 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    /// Handle \E <seq1> sequences
266    // https://invisible-island.net/xterm/xterm-function-keys.html
267    fn escape_sequence(&mut self) -> Result<KeyEvent> {
268        self._do_escape_sequence(true)
269    }
270
271    /// Don't call directly, call `PosixRawReader::escape_sequence` instead
272    fn _do_escape_sequence(&mut self, allow_recurse: bool) -> Result<KeyEvent> {
273        // Read the next byte representing the escape sequence.
274        let seq1 = self.next_char()?;
275        if seq1 == '[' {
276            // \E[ sequences. (CSI)
277            self.escape_csi()
278        } else if seq1 == 'O' {
279            // xterm
280            // \EO sequences. (SS3)
281            self.escape_o()
282        } else if seq1 == '\x1b' {
283            // \E\E — used by rxvt, iTerm (under default config), etc.
284            // ```
285            // \E\E[A => Alt-Up
286            // \E\E[B => Alt-Down
287            // \E\E[C => Alt-Right
288            // \E\E[D => Alt-Left
289            // ```
290            //
291            // In general this more or less works just adding ALT to an existing
292            // key, but has a wrinkle in that `ESC ESC` without anything
293            // following should be interpreted as the the escape key.
294            //
295            // We handle this by polling to see if there's anything coming
296            // within our timeout, and if so, recursing once, but adding alt to
297            // what we read.
298            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                // Ignore poll errors, it's very likely we'll pick them up on
308                // the next read anyway.
309                Ok(0) | Err(_) => Ok(E::ESC),
310                Ok(n) => {
311                    debug_assert!(n > 0, "{}", n);
312                    // recurse, and add the alt modifier.
313                    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    /// Handle \E[ <seq2> escape sequences
323    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                    // Extended escape, read additional byte.
333                    self.extended_escape(seq2)
334                }
335            }
336        } else if seq2 == '[' {
337            let seq3 = self.next_char()?;
338            // Linux console
339            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            // ANSI
352            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                //'E' => E(K::, M::), // Ignore
358                END => E(K::End, M::NONE),
359                //'G' => E(K::, M::), // Ignore
360                HOME => E(K::Home, M::NONE), // khome
361                //'J' => E(K::, M::), // clr_eos
362                //'K' => E(K::, M::), // clr_eol
363                //'L' => E(K::, M::), // il1
364                //'M' => E(K::, M::), // kmous
365                //'P' => E(K::Delete, M::NONE), // dch1
366                'Z' => E(K::BackTab, M::NONE),
367                'a' => E(K::Up, M::SHIFT),    // rxvt: kind or kUP
368                'b' => E(K::Down, M::SHIFT),  // rxvt: kri or kDN
369                'c' => E(K::Right, M::SHIFT), // rxvt
370                'd' => E(K::Left, M::SHIFT),  // rxvt
371                _ => {
372                    debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2);
373                    E(K::UnknownEscSeq, M::NONE)
374                }
375            })
376        }
377    }
378
379    /// Handle \E[ <seq2:digit> escape sequences
380    #[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), // tmux, xrvt
386                INSERT => E(K::Insert, M::NONE),
387                DELETE => E(K::Delete, M::NONE),
388                '4' | RXVT_END => E(K::End, M::NONE), // tmux, xrvt
389                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),  // rxvt-unicode
402                    ('1', '2') => E(K::F(2), M::NONE),  // rxvt-unicode
403                    ('1', '3') => E(K::F(3), M::NONE),  // rxvt-unicode
404                    ('1', '4') => E(K::F(4), M::NONE),  // rxvt-unicode
405                    ('1', '5') => E(K::F(5), M::NONE),  // kf5
406                    ('1', '7') => E(K::F(6), M::NONE),  // kf6
407                    ('1', '8') => E(K::F(7), M::NONE),  // kf7
408                    ('1', '9') => E(K::F(8), M::NONE),  // kf8
409                    ('2', '0') => E(K::F(9), M::NONE),  // kf9
410                    ('2', '1') => E(K::F(10), M::NONE), // kf10
411                    ('2', '3') => E(K::F(11), M::NONE), // kf11
412                    ('2', '4') => E(K::F(12), M::NONE), // kf12
413                    //('6', '2') => KeyCode::ScrollUp,
414                    //('6', '3') => KeyCode::ScrollDown,
415                    _ => {
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()?; // 'R' expected
427                        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', '5', '6') => E(K::F(17), M::CTRL),
434                            ('1', '7', CTRL) => E(K::F(6), M::CTRL),
435                            //('1', '7', '6') => E(K::F(18), M::CTRL),
436                            ('1', '8', CTRL) => E(K::F(7), M::CTRL),
437                            ('1', '9', CTRL) => E(K::F(8), M::CTRL),
438                            //('1', '9', '6') => E(K::F(19), M::CTRL),
439                            ('2', '0', CTRL) => E(K::F(9), M::CTRL),
440                            //('2', '0', '6') => E(K::F(21), M::CTRL),
441                            ('2', '1', CTRL) => E(K::F(10), M::CTRL),
442                            //('2', '1', '6') => E(K::F(22), M::CTRL),
443                            ('2', '3', CTRL) => E(K::F(11), M::CTRL),
444                            //('2', '3', '6') => E(K::F(23), M::CTRL),
445                            ('2', '4', CTRL) => E(K::F(12), M::CTRL),
446                            //('2', '4', '6') => E(K::F(24), M::CTRL),
447                            _ => {
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()?; // 'R' expected
491                                       //('1', '0', UP) => E(K::, M::), // Alt + Shift + Up
492                    Ok(E(K::UnknownEscSeq, M::NONE))
493                } else if seq2 == '1' {
494                    Ok(match (seq4, seq5) {
495                        (SHIFT, UP) => E(K::Up, M::SHIFT),     // ~ key_sr
496                        (SHIFT, DOWN) => E(K::Down, M::SHIFT), // ~ key_sf
497                        (SHIFT, RIGHT) => E(K::Right, M::SHIFT),
498                        (SHIFT, LEFT) => E(K::Left, M::SHIFT),
499                        (SHIFT, END) => E(K::End, M::SHIFT), // kEND
500                        (SHIFT, HOME) => E(K::Home, M::SHIFT), // kHOM
501                        //('2', 'P') => E(K::F(13), M::NONE),
502                        //('2', 'Q') => E(K::F(14), M::NONE),
503                        //('2', 'S') => E(K::F(16), M::NONE),
504                        (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                        //('6', 'P') => E(K::F(13), M::CTRL),
542                        //('6', 'Q') => E(K::F(14), M::CTRL),
543                        //('6', 'S') => E(K::F(16), M::CTRL),
544                        (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                        // Meta + arrow on (some?) Macs when using iTerm defaults
587                        ('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), // kEND5 or kel
661                (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    /// Handle \EO <seq2> escape sequences
673    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            //'E' => E(K::, M::),// key_b2, kb2
681            END => E(K::End, M::NONE),   // kend
682            HOME => E(K::Home, M::NONE), // khome
683            'M' => E::ENTER,             // kent
684            'P' => E(K::F(1), M::NONE),  // kf1
685            'Q' => E(K::F(2), M::NONE),  // kf2
686            'R' => E(K::F(3), M::NONE),  // kf3
687            'S' => E(K::F(4), M::NONE),  // kf4
688            'a' => E(K::Up, M::CTRL),
689            'b' => E(K::Down, M::CTRL),
690            'c' => E(K::Right, M::CTRL), // rxvt
691            'd' => E(K::Left, M::CTRL),  // rxvt
692            'l' => E(K::F(8), M::NONE),
693            't' => E(K::F(5), M::NONE),  // kf5 or kb1
694            'u' => E(K::F(6), M::NONE),  // kf6 or kb2
695            'v' => E(K::F(7), M::NONE),  // kf7 or kb3
696            'w' => E(K::F(9), M::NONE),  // kf9 or ka1
697            'x' => E(K::F(10), M::NONE), // kf10 or ka2
698            _ => {
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) // Ignore EINTR while polling
719                }
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                // prefer user input over external print
760                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                    // single escape
805                }
806                Ok(_) => {
807                    // escape sequence
808                    key = self.escape_sequence()?
809                }
810                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
811                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; // TODO validate
849                    }
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    /// Called whenever a code point is parsed successfully
881    fn codepoint(&mut self, c: char) {
882        self.c = Some(c);
883        self.valid = true;
884    }
885
886    /// Called when an invalid_sequence is detected
887    fn invalid_sequence(&mut self) {
888        self.c = None;
889        self.valid = false;
890    }
891}
892
893/// Console output writer
894pub struct PosixRenderer {
895    out: RawFd,
896    cols: usize, // Number of columns in terminal
897    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        // old_rows < cursor_row if the prompt spans multiple lines and if
921        // this is the default State.
922        let cursor_row_movement = old_rows.saturating_sub(current_row);
923        // move the cursor down as required
924        if cursor_row_movement > 0 {
925            write!(self.buffer, "\x1b[{cursor_row_movement}B").unwrap();
926        }
927        // clear old rows
928        for _ in 0..old_rows {
929            self.buffer.push_str("\r\x1b[K\x1b[A");
930        }
931        // clear the line
932        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            // move down
945            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            // move up
953            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            // move right
963            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            // move left
971            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            // display the prompt
1002            self.buffer
1003                .push_str(&highlighter.highlight_prompt(prompt, default_prompt));
1004            // display the input line
1005            self.buffer
1006                .push_str(&highlighter.highlight(line, line.pos()));
1007        } else {
1008            // display the prompt
1009            self.buffer.push_str(prompt);
1010            // display the input line
1011            self.buffer.push_str(line);
1012        }
1013        // display hint
1014        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        // we have to generate our own newline on line wrap
1022        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        // position the cursor
1031        let new_cursor_row_movement = end_pos.row - cursor.row;
1032        // move the cursor up as required
1033        if new_cursor_row_movement > 0 {
1034            write!(self.buffer, "\x1b[{new_cursor_row_movement}A")?;
1035        }
1036        // position the cursor within the line
1037        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    /// Control characters are treated as having zero width.
1053    /// Characters with 2 column width are correctly handled (not split).
1054    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    /// Clear the screen. Used to handle ctrl+l
1089    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    /// Try to update the number of columns in the current terminal,
1101    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    /// Try to get the number of rows in the current terminal,
1111    /// or assume 24 if it fails.
1112    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        /* Report cursor location */
1127        self.write_and_flush("\x1b[6n")?;
1128        /* Read the response: ESC [ rows ; cols R */
1129        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    // external print reader
1273    pipe_reader: Option<PipeReader>,
1274    // external print writer
1275    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); // TODO: useless ?
1313                    (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    // Init checks:
1359
1360    /// Check if current terminal can provide a rich line-editing user
1361    /// interface.
1362    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    // Interactive loop:
1375
1376    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        // enable bracketed paste
1385        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        // when all ExternalPrinter are dropped there is no need to use `pipe_reader`
1395        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    /// Create a RAW reader
1412    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); // TODO validate: bound
1455        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        // write directly to stdout/stderr while not in raw mode
1499        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))?; // FIXME
1506            writer.write_all(&[b'm'])?;
1507            writer.flush()?;
1508        } else {
1509            return Err(io::Error::from(ErrorKind::Other).into()); // FIXME
1510        }
1511        Ok(())
1512    }
1513}
1514
1515#[cfg(not(test))]
1516pub fn suspend() -> Result<()> {
1517    use nix::sys::signal;
1518    use nix::unistd::Pid;
1519    // suspend the whole process group
1520    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        // disable BREAK interrupt, CR to NL conversion on input,
1543        // input parity check, strip high bit (bit 8), output flow control
1544        raw.input_flags &= !(InputFlags::BRKINT
1545            | InputFlags::ICRNL
1546            | InputFlags::INPCK
1547            | InputFlags::ISTRIP
1548            | InputFlags::IXON);
1549        // we don't want raw output, it turns newlines into straight line feeds
1550        // disable all output processing
1551        // raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);
1552
1553        // character-size mark (8 bits)
1554        raw.control_flags |= ControlFlags::CS8;
1555        // disable echoing, canonical mode, extended input processing and signals
1556        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; // One character-at-a-time input
1564        raw.control_chars[SCI::VTIME as usize] = 0; // with blocking read
1565
1566        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        // disable BREAK interrupt, CR to NL conversion on input,
1603        // input parity check, strip high bit (bit 8), output flow control
1604        raw.c_iflag &=
1605            !(termios::BRKINT | termios::ICRNL | termios::INPCK | termios::ISTRIP | termios::IXON);
1606        // we don't want raw output, it turns newlines into straight line feeds
1607        // disable all output processing
1608        // raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);
1609
1610        // character-size mark (8 bits)
1611        raw.c_cflag |= termios::CS8;
1612        // disable echoing, canonical mode, extended input processing and signals
1613        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; // One character-at-a-time input
1620        raw.c_cc[termios::VTIME] = 0; // with blocking read
1621
1622        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}