rustyline/tty/
unix.rs

1//! Unix specific definitions
2use std::cmp;
3use std::collections::HashMap;
4use std::fs::{File, OpenOptions};
5use std::io::{self, BufReader, ErrorKind, Read, Write};
6use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
7use std::os::unix::net::UnixStream;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::sync::mpsc::{self, SyncSender};
10use std::sync::{Arc, Mutex};
11
12use log::{debug, warn};
13use nix::errno::Errno;
14use nix::poll::{self, PollFlags};
15use nix::sys::select::{self, FdSet};
16use nix::sys::termios::{self, SetArg, SpecialCharacterIndices as SCI, Termios};
17use nix::unistd::{close, isatty, read, write};
18use unicode_segmentation::UnicodeSegmentation;
19use utf8parse::{Parser, Receiver};
20
21use super::{width, Event, RawMode, RawReader, Renderer, Term};
22use crate::config::{Behavior, BellStyle, ColorMode, Config};
23use crate::highlight::Highlighter;
24use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
25use crate::layout::{Layout, Position};
26use crate::line_buffer::LineBuffer;
27use crate::{error, Cmd, ReadlineError, Result};
28
29/// Unsupported Terminals that don't support RAW mode
30const UNSUPPORTED_TERM: [&str; 3] = ["dumb", "cons25", "emacs"];
31
32const BRACKETED_PASTE_ON: &str = "\x1b[?2004h";
33const BRACKETED_PASTE_OFF: &str = "\x1b[?2004l";
34
35nix::ioctl_read_bad!(win_size, libc::TIOCGWINSZ, libc::winsize);
36
37#[allow(clippy::useless_conversion)]
38fn get_win_size(fd: RawFd) -> (usize, usize) {
39    use std::mem::zeroed;
40
41    if cfg!(test) {
42        return (80, 24);
43    }
44
45    unsafe {
46        let mut size: libc::winsize = zeroed();
47        match win_size(fd, &mut size) {
48            Ok(0) => {
49                // In linux pseudo-terminals are created with dimensions of
50                // zero. If host application didn't initialize the correct
51                // size before start we treat zero size as 80 columns and
52                // infinite rows
53                let cols = if size.ws_col == 0 {
54                    80
55                } else {
56                    size.ws_col as usize
57                };
58                let rows = if size.ws_row == 0 {
59                    usize::MAX
60                } else {
61                    size.ws_row as usize
62                };
63                (cols, rows)
64            }
65            _ => (80, 24),
66        }
67    }
68}
69
70/// Check TERM environment variable to see if current term is in our
71/// unsupported list
72fn is_unsupported_term() -> bool {
73    match std::env::var("TERM") {
74        Ok(term) => {
75            for iter in &UNSUPPORTED_TERM {
76                if (*iter).eq_ignore_ascii_case(&term) {
77                    return true;
78                }
79            }
80            false
81        }
82        Err(_) => false,
83    }
84}
85
86/// Return whether or not STDIN, STDOUT or STDERR is a TTY
87fn is_a_tty(fd: RawFd) -> bool {
88    isatty(fd).unwrap_or(false)
89}
90
91pub type PosixKeyMap = HashMap<KeyEvent, Cmd>;
92#[cfg(not(test))]
93pub type KeyMap = PosixKeyMap;
94
95#[must_use = "You must restore default mode (disable_raw_mode)"]
96pub struct PosixMode {
97    termios: Termios,
98    tty_in: RawFd,
99    tty_out: Option<RawFd>,
100    raw_mode: Arc<AtomicBool>,
101}
102
103#[cfg(not(test))]
104pub type Mode = PosixMode;
105
106impl RawMode for PosixMode {
107    /// Disable RAW mode for the terminal.
108    fn disable_raw_mode(&self) -> Result<()> {
109        termios::tcsetattr(self.tty_in, SetArg::TCSADRAIN, &self.termios)?;
110        // disable bracketed paste
111        if let Some(out) = self.tty_out {
112            write_all(out, BRACKETED_PASTE_OFF)?;
113        }
114        self.raw_mode.store(false, Ordering::SeqCst);
115        Ok(())
116    }
117}
118
119// Rust std::io::Stdin is buffered with no way to know if bytes are available.
120// So we use low-level stuff instead...
121struct TtyIn {
122    fd: RawFd,
123    sigwinch_pipe: Option<RawFd>,
124}
125
126impl Read for TtyIn {
127    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
128        loop {
129            let res = unsafe {
130                libc::read(
131                    self.fd,
132                    buf.as_mut_ptr() as *mut libc::c_void,
133                    buf.len() as libc::size_t,
134                )
135            };
136            if res == -1 {
137                let error = io::Error::last_os_error();
138                if error.kind() == ErrorKind::Interrupted && self.sigwinch()? {
139                    return Err(io::Error::new(
140                        ErrorKind::Interrupted,
141                        error::WindowResizedError,
142                    ));
143                } else if error.kind() != ErrorKind::Interrupted {
144                    return Err(error);
145                }
146            } else {
147                #[allow(clippy::cast_sign_loss)]
148                return Ok(res as usize);
149            }
150        }
151    }
152}
153
154impl TtyIn {
155    /// Check if a SIGWINCH signal has been received
156    fn sigwinch(&self) -> nix::Result<bool> {
157        if let Some(pipe) = self.sigwinch_pipe {
158            let mut buf = [0u8; 64];
159            match read(pipe, &mut buf) {
160                Ok(0) => Ok(false),
161                Ok(_) => Ok(true),
162                Err(e) if e == Errno::EWOULDBLOCK || e == Errno::EINTR => Ok(false),
163                Err(e) => Err(e),
164            }
165        } else {
166            Ok(false)
167        }
168    }
169}
170
171// (native receiver with a selectable file descriptor, actual message receiver)
172type PipeReader = Arc<Mutex<(File, mpsc::Receiver<String>)>>;
173// (native sender, actual message sender)
174type PipeWriter = (Arc<Mutex<File>>, SyncSender<String>);
175
176/// Console input reader
177pub struct PosixRawReader {
178    tty_in: BufReader<TtyIn>,
179    timeout_ms: i32,
180    parser: Parser,
181    key_map: PosixKeyMap,
182    // external print reader
183    pipe_reader: Option<PipeReader>,
184    fds: FdSet,
185}
186
187impl AsRawFd for PosixRawReader {
188    fn as_raw_fd(&self) -> RawFd {
189        self.tty_in.get_ref().fd
190    }
191}
192
193struct Utf8 {
194    c: Option<char>,
195    valid: bool,
196}
197
198const UP: char = 'A'; // kcuu1, kUP*
199const DOWN: char = 'B'; // kcud1, kDN*
200const RIGHT: char = 'C'; // kcuf1, kRIT*
201const LEFT: char = 'D'; // kcub1, kLFT*
202const END: char = 'F'; // kend*
203const HOME: char = 'H'; // khom*
204const INSERT: char = '2'; // kic*
205const DELETE: char = '3'; // kdch1, kDC*
206const PAGE_UP: char = '5'; // kpp, kPRV*
207const PAGE_DOWN: char = '6'; // knp, kNXT*
208
209const RXVT_HOME: char = '7';
210const RXVT_END: char = '8';
211
212const SHIFT: char = '2';
213const ALT: char = '3';
214const ALT_SHIFT: char = '4';
215const CTRL: char = '5';
216const CTRL_SHIFT: char = '6';
217const CTRL_ALT: char = '7';
218const CTRL_ALT_SHIFT: char = '8';
219
220const RXVT_SHIFT: char = '$';
221const RXVT_CTRL: char = '\x1e';
222const RXVT_CTRL_SHIFT: char = '@';
223
224impl PosixRawReader {
225    fn new(
226        fd: RawFd,
227        sigwinch_pipe: Option<RawFd>,
228        config: &Config,
229        key_map: PosixKeyMap,
230        pipe_reader: Option<PipeReader>,
231    ) -> Self {
232        Self {
233            tty_in: BufReader::with_capacity(1024, TtyIn { fd, sigwinch_pipe }),
234            timeout_ms: config.keyseq_timeout(),
235            parser: Parser::new(),
236            key_map,
237            pipe_reader,
238            fds: FdSet::new(),
239        }
240    }
241
242    /// Handle \E <seq1> sequences
243    // https://invisible-island.net/xterm/xterm-function-keys.html
244    fn escape_sequence(&mut self) -> Result<KeyEvent> {
245        self._do_escape_sequence(true)
246    }
247
248    /// Don't call directly, call `PosixRawReader::escape_sequence` instead
249    fn _do_escape_sequence(&mut self, allow_recurse: bool) -> Result<KeyEvent> {
250        // Read the next byte representing the escape sequence.
251        let seq1 = self.next_char()?;
252        if seq1 == '[' {
253            // \E[ sequences. (CSI)
254            self.escape_csi()
255        } else if seq1 == 'O' {
256            // xterm
257            // \EO sequences. (SS3)
258            self.escape_o()
259        } else if seq1 == '\x1b' {
260            // \E\E — used by rxvt, iTerm (under default config), etc.
261            // ```
262            // \E\E[A => Alt-Up
263            // \E\E[B => Alt-Down
264            // \E\E[C => Alt-Right
265            // \E\E[D => Alt-Left
266            // ```
267            //
268            // In general this more or less works just adding ALT to an existing
269            // key, but has a wrinkle in that `ESC ESC` without anything
270            // following should be interpreted as the the escape key.
271            //
272            // We handle this by polling to see if there's anything coming
273            // within our timeout, and if so, recursing once, but adding alt to
274            // what we read.
275            if !allow_recurse {
276                return Ok(E::ESC);
277            }
278            let timeout = if self.timeout_ms < 0 {
279                100
280            } else {
281                self.timeout_ms
282            };
283            match self.poll(timeout) {
284                // Ignore poll errors, it's very likely we'll pick them up on
285                // the next read anyway.
286                Ok(0) | Err(_) => Ok(E::ESC),
287                Ok(n) => {
288                    debug_assert!(n > 0, "{}", n);
289                    // recurse, and add the alt modifier.
290                    let E(k, m) = self._do_escape_sequence(false)?;
291                    Ok(E(k, m | M::ALT))
292                }
293            }
294        } else {
295            Ok(E::alt(seq1))
296        }
297    }
298
299    /// Handle \E[ <seq2> escape sequences
300    fn escape_csi(&mut self) -> Result<KeyEvent> {
301        let seq2 = self.next_char()?;
302        if seq2.is_ascii_digit() {
303            match seq2 {
304                '0' | '9' => {
305                    debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2);
306                    Ok(E(K::UnknownEscSeq, M::NONE))
307                }
308                _ => {
309                    // Extended escape, read additional byte.
310                    self.extended_escape(seq2)
311                }
312            }
313        } else if seq2 == '[' {
314            let seq3 = self.next_char()?;
315            // Linux console
316            Ok(match seq3 {
317                'A' => E(K::F(1), M::NONE),
318                'B' => E(K::F(2), M::NONE),
319                'C' => E(K::F(3), M::NONE),
320                'D' => E(K::F(4), M::NONE),
321                'E' => E(K::F(5), M::NONE),
322                _ => {
323                    debug!(target: "rustyline", "unsupported esc sequence: \\E[[{:?}", seq3);
324                    E(K::UnknownEscSeq, M::NONE)
325                }
326            })
327        } else {
328            // ANSI
329            Ok(match seq2 {
330                UP => E(K::Up, M::NONE),
331                DOWN => E(K::Down, M::NONE),
332                RIGHT => E(K::Right, M::NONE),
333                LEFT => E(K::Left, M::NONE),
334                //'E' => E(K::, M::), // Ignore
335                END => E(K::End, M::NONE),
336                //'G' => E(K::, M::), // Ignore
337                HOME => E(K::Home, M::NONE), // khome
338                //'J' => E(K::, M::), // clr_eos
339                //'K' => E(K::, M::), // clr_eol
340                //'L' => E(K::, M::), // il1
341                //'M' => E(K::, M::), // kmous
342                //'P' => E(K::Delete, M::NONE), // dch1
343                'Z' => E(K::BackTab, M::NONE),
344                'a' => E(K::Up, M::SHIFT),    // rxvt: kind or kUP
345                'b' => E(K::Down, M::SHIFT),  // rxvt: kri or kDN
346                'c' => E(K::Right, M::SHIFT), // rxvt
347                'd' => E(K::Left, M::SHIFT),  // rxvt
348                _ => {
349                    debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2);
350                    E(K::UnknownEscSeq, M::NONE)
351                }
352            })
353        }
354    }
355
356    /// Handle \E[ <seq2:digit> escape sequences
357    #[allow(clippy::cognitive_complexity)]
358    fn extended_escape(&mut self, seq2: char) -> Result<KeyEvent> {
359        let seq3 = self.next_char()?;
360        if seq3 == '~' {
361            Ok(match seq2 {
362                '1' | RXVT_HOME => E(K::Home, M::NONE), // tmux, xrvt
363                INSERT => E(K::Insert, M::NONE),
364                DELETE => E(K::Delete, M::NONE),
365                '4' | RXVT_END => E(K::End, M::NONE), // tmux, xrvt
366                PAGE_UP => E(K::PageUp, M::NONE),
367                PAGE_DOWN => E(K::PageDown, M::NONE),
368                _ => {
369                    debug!(target: "rustyline",
370                           "unsupported esc sequence: \\E[{}~", seq2);
371                    E(K::UnknownEscSeq, M::NONE)
372                }
373            })
374        } else if seq3.is_ascii_digit() {
375            let seq4 = self.next_char()?;
376            if seq4 == '~' {
377                Ok(match (seq2, seq3) {
378                    ('1', '1') => E(K::F(1), M::NONE),  // rxvt-unicode
379                    ('1', '2') => E(K::F(2), M::NONE),  // rxvt-unicode
380                    ('1', '3') => E(K::F(3), M::NONE),  // rxvt-unicode
381                    ('1', '4') => E(K::F(4), M::NONE),  // rxvt-unicode
382                    ('1', '5') => E(K::F(5), M::NONE),  // kf5
383                    ('1', '7') => E(K::F(6), M::NONE),  // kf6
384                    ('1', '8') => E(K::F(7), M::NONE),  // kf7
385                    ('1', '9') => E(K::F(8), M::NONE),  // kf8
386                    ('2', '0') => E(K::F(9), M::NONE),  // kf9
387                    ('2', '1') => E(K::F(10), M::NONE), // kf10
388                    ('2', '3') => E(K::F(11), M::NONE), // kf11
389                    ('2', '4') => E(K::F(12), M::NONE), // kf12
390                    //('6', '2') => KeyCode::ScrollUp,
391                    //('6', '3') => KeyCode::ScrollDown,
392                    _ => {
393                        debug!(target: "rustyline",
394                               "unsupported esc sequence: \\E[{}{}~", seq2, seq3);
395                        E(K::UnknownEscSeq, M::NONE)
396                    }
397                })
398            } else if seq4 == ';' {
399                let seq5 = self.next_char()?;
400                if seq5.is_ascii_digit() {
401                    let seq6 = self.next_char()?;
402                    if seq6.is_ascii_digit() {
403                        self.next_char()?; // 'R' expected
404                        Ok(E(K::UnknownEscSeq, M::NONE))
405                    } else if seq6 == 'R' {
406                        Ok(E(K::UnknownEscSeq, M::NONE))
407                    } else if seq6 == '~' {
408                        Ok(match (seq2, seq3, seq5) {
409                            ('1', '5', CTRL) => E(K::F(5), M::CTRL),
410                            //('1', '5', '6') => E(K::F(17), M::CTRL),
411                            ('1', '7', CTRL) => E(K::F(6), M::CTRL),
412                            //('1', '7', '6') => E(K::F(18), M::CTRL),
413                            ('1', '8', CTRL) => E(K::F(7), M::CTRL),
414                            ('1', '9', CTRL) => E(K::F(8), M::CTRL),
415                            //('1', '9', '6') => E(K::F(19), M::CTRL),
416                            ('2', '0', CTRL) => E(K::F(9), M::CTRL),
417                            //('2', '0', '6') => E(K::F(21), M::CTRL),
418                            ('2', '1', CTRL) => E(K::F(10), M::CTRL),
419                            //('2', '1', '6') => E(K::F(22), M::CTRL),
420                            ('2', '3', CTRL) => E(K::F(11), M::CTRL),
421                            //('2', '3', '6') => E(K::F(23), M::CTRL),
422                            ('2', '4', CTRL) => E(K::F(12), M::CTRL),
423                            //('2', '4', '6') => E(K::F(24), M::CTRL),
424                            _ => {
425                                debug!(target: "rustyline",
426                                       "unsupported esc sequence: \\E[{}{};{}~", seq2, seq3, seq5);
427                                E(K::UnknownEscSeq, M::NONE)
428                            }
429                        })
430                    } else {
431                        debug!(target: "rustyline",
432                               "unsupported esc sequence: \\E[{}{};{}{}", seq2, seq3, seq5, seq6);
433                        Ok(E(K::UnknownEscSeq, M::NONE))
434                    }
435                } else {
436                    debug!(target: "rustyline",
437                           "unsupported esc sequence: \\E[{}{};{:?}", seq2, seq3, seq5);
438                    Ok(E(K::UnknownEscSeq, M::NONE))
439                }
440            } else if seq4.is_ascii_digit() {
441                let seq5 = self.next_char()?;
442                if seq5 == '~' {
443                    Ok(match (seq2, seq3, seq4) {
444                        ('2', '0', '0') => E(K::BracketedPasteStart, M::NONE),
445                        ('2', '0', '1') => E(K::BracketedPasteEnd, M::NONE),
446                        _ => {
447                            debug!(target: "rustyline",
448                                   "unsupported esc sequence: \\E[{}{}{}~", seq2, seq3, seq4);
449                            E(K::UnknownEscSeq, M::NONE)
450                        }
451                    })
452                } else {
453                    debug!(target: "rustyline",
454                           "unsupported esc sequence: \\E[{}{}{}{}", seq2, seq3, seq4, seq5);
455                    Ok(E(K::UnknownEscSeq, M::NONE))
456                }
457            } else {
458                debug!(target: "rustyline",
459                       "unsupported esc sequence: \\E[{}{}{:?}", seq2, seq3, seq4);
460                Ok(E(K::UnknownEscSeq, M::NONE))
461            }
462        } else if seq3 == ';' {
463            let seq4 = self.next_char()?;
464            if seq4.is_ascii_digit() {
465                let seq5 = self.next_char()?;
466                if seq5.is_ascii_digit() {
467                    self.next_char()?; // 'R' expected
468                                       //('1', '0', UP) => E(K::, M::), // Alt + Shift + Up
469                    Ok(E(K::UnknownEscSeq, M::NONE))
470                } else if seq2 == '1' {
471                    Ok(match (seq4, seq5) {
472                        (SHIFT, UP) => E(K::Up, M::SHIFT),     // ~ key_sr
473                        (SHIFT, DOWN) => E(K::Down, M::SHIFT), // ~ key_sf
474                        (SHIFT, RIGHT) => E(K::Right, M::SHIFT),
475                        (SHIFT, LEFT) => E(K::Left, M::SHIFT),
476                        (SHIFT, END) => E(K::End, M::SHIFT), // kEND
477                        (SHIFT, HOME) => E(K::Home, M::SHIFT), // kHOM
478                        //('2', 'P') => E(K::F(13), M::NONE),
479                        //('2', 'Q') => E(K::F(14), M::NONE),
480                        //('2', 'S') => E(K::F(16), M::NONE),
481                        (ALT, UP) => E(K::Up, M::ALT),
482                        (ALT, DOWN) => E(K::Down, M::ALT),
483                        (ALT, RIGHT) => E(K::Right, M::ALT),
484                        (ALT, LEFT) => E(K::Left, M::ALT),
485                        (ALT, END) => E(K::End, M::ALT),
486                        (ALT, HOME) => E(K::Home, M::ALT),
487                        (ALT_SHIFT, UP) => E(K::Up, M::ALT_SHIFT),
488                        (ALT_SHIFT, DOWN) => E(K::Down, M::ALT_SHIFT),
489                        (ALT_SHIFT, RIGHT) => E(K::Right, M::ALT_SHIFT),
490                        (ALT_SHIFT, LEFT) => E(K::Left, M::ALT_SHIFT),
491                        (ALT_SHIFT, END) => E(K::End, M::ALT_SHIFT),
492                        (ALT_SHIFT, HOME) => E(K::Home, M::ALT_SHIFT),
493                        (CTRL, UP) => E(K::Up, M::CTRL),
494                        (CTRL, DOWN) => E(K::Down, M::CTRL),
495                        (CTRL, RIGHT) => E(K::Right, M::CTRL),
496                        (CTRL, LEFT) => E(K::Left, M::CTRL),
497                        (CTRL, END) => E(K::End, M::CTRL),
498                        (CTRL, HOME) => E(K::Home, M::CTRL),
499                        (CTRL, 'P') => E(K::F(1), M::CTRL),
500                        (CTRL, 'Q') => E(K::F(2), M::CTRL),
501                        (CTRL, 'S') => E(K::F(4), M::CTRL),
502                        (CTRL, 'p') => E(K::Char('0'), M::CTRL),
503                        (CTRL, 'q') => E(K::Char('1'), M::CTRL),
504                        (CTRL, 'r') => E(K::Char('2'), M::CTRL),
505                        (CTRL, 's') => E(K::Char('3'), M::CTRL),
506                        (CTRL, 't') => E(K::Char('4'), M::CTRL),
507                        (CTRL, 'u') => E(K::Char('5'), M::CTRL),
508                        (CTRL, 'v') => E(K::Char('6'), M::CTRL),
509                        (CTRL, 'w') => E(K::Char('7'), M::CTRL),
510                        (CTRL, 'x') => E(K::Char('8'), M::CTRL),
511                        (CTRL, 'y') => E(K::Char('9'), M::CTRL),
512                        (CTRL_SHIFT, UP) => E(K::Up, M::CTRL_SHIFT),
513                        (CTRL_SHIFT, DOWN) => E(K::Down, M::CTRL_SHIFT),
514                        (CTRL_SHIFT, RIGHT) => E(K::Right, M::CTRL_SHIFT),
515                        (CTRL_SHIFT, LEFT) => E(K::Left, M::CTRL_SHIFT),
516                        (CTRL_SHIFT, END) => E(K::End, M::CTRL_SHIFT),
517                        (CTRL_SHIFT, HOME) => E(K::Home, M::CTRL_SHIFT),
518                        //('6', 'P') => E(K::F(13), M::CTRL),
519                        //('6', 'Q') => E(K::F(14), M::CTRL),
520                        //('6', 'S') => E(K::F(16), M::CTRL),
521                        (CTRL_SHIFT, 'p') => E(K::Char('0'), M::CTRL_SHIFT),
522                        (CTRL_SHIFT, 'q') => E(K::Char('1'), M::CTRL_SHIFT),
523                        (CTRL_SHIFT, 'r') => E(K::Char('2'), M::CTRL_SHIFT),
524                        (CTRL_SHIFT, 's') => E(K::Char('3'), M::CTRL_SHIFT),
525                        (CTRL_SHIFT, 't') => E(K::Char('4'), M::CTRL_SHIFT),
526                        (CTRL_SHIFT, 'u') => E(K::Char('5'), M::CTRL_SHIFT),
527                        (CTRL_SHIFT, 'v') => E(K::Char('6'), M::CTRL_SHIFT),
528                        (CTRL_SHIFT, 'w') => E(K::Char('7'), M::CTRL_SHIFT),
529                        (CTRL_SHIFT, 'x') => E(K::Char('8'), M::CTRL_SHIFT),
530                        (CTRL_SHIFT, 'y') => E(K::Char('9'), M::CTRL_SHIFT),
531                        (CTRL_ALT, UP) => E(K::Up, M::CTRL_ALT),
532                        (CTRL_ALT, DOWN) => E(K::Down, M::CTRL_ALT),
533                        (CTRL_ALT, RIGHT) => E(K::Right, M::CTRL_ALT),
534                        (CTRL_ALT, LEFT) => E(K::Left, M::CTRL_ALT),
535                        (CTRL_ALT, END) => E(K::End, M::CTRL_ALT),
536                        (CTRL_ALT, HOME) => E(K::Home, M::CTRL_ALT),
537                        (CTRL_ALT, 'p') => E(K::Char('0'), M::CTRL_ALT),
538                        (CTRL_ALT, 'q') => E(K::Char('1'), M::CTRL_ALT),
539                        (CTRL_ALT, 'r') => E(K::Char('2'), M::CTRL_ALT),
540                        (CTRL_ALT, 's') => E(K::Char('3'), M::CTRL_ALT),
541                        (CTRL_ALT, 't') => E(K::Char('4'), M::CTRL_ALT),
542                        (CTRL_ALT, 'u') => E(K::Char('5'), M::CTRL_ALT),
543                        (CTRL_ALT, 'v') => E(K::Char('6'), M::CTRL_ALT),
544                        (CTRL_ALT, 'w') => E(K::Char('7'), M::CTRL_ALT),
545                        (CTRL_ALT, 'x') => E(K::Char('8'), M::CTRL_ALT),
546                        (CTRL_ALT, 'y') => E(K::Char('9'), M::CTRL_ALT),
547                        (CTRL_ALT_SHIFT, UP) => E(K::Up, M::CTRL_ALT_SHIFT),
548                        (CTRL_ALT_SHIFT, DOWN) => E(K::Down, M::CTRL_ALT_SHIFT),
549                        (CTRL_ALT_SHIFT, RIGHT) => E(K::Right, M::CTRL_ALT_SHIFT),
550                        (CTRL_ALT_SHIFT, LEFT) => E(K::Left, M::CTRL_ALT_SHIFT),
551                        (CTRL_ALT_SHIFT, END) => E(K::End, M::CTRL_ALT_SHIFT),
552                        (CTRL_ALT_SHIFT, HOME) => E(K::Home, M::CTRL_ALT_SHIFT),
553                        (CTRL_ALT_SHIFT, 'p') => E(K::Char('0'), M::CTRL_ALT_SHIFT),
554                        (CTRL_ALT_SHIFT, 'q') => E(K::Char('1'), M::CTRL_ALT_SHIFT),
555                        (CTRL_ALT_SHIFT, 'r') => E(K::Char('2'), M::CTRL_ALT_SHIFT),
556                        (CTRL_ALT_SHIFT, 's') => E(K::Char('3'), M::CTRL_ALT_SHIFT),
557                        (CTRL_ALT_SHIFT, 't') => E(K::Char('4'), M::CTRL_ALT_SHIFT),
558                        (CTRL_ALT_SHIFT, 'u') => E(K::Char('5'), M::CTRL_ALT_SHIFT),
559                        (CTRL_ALT_SHIFT, 'v') => E(K::Char('6'), M::CTRL_ALT_SHIFT),
560                        (CTRL_ALT_SHIFT, 'w') => E(K::Char('7'), M::CTRL_ALT_SHIFT),
561                        (CTRL_ALT_SHIFT, 'x') => E(K::Char('8'), M::CTRL_ALT_SHIFT),
562                        (CTRL_ALT_SHIFT, 'y') => E(K::Char('9'), M::CTRL_ALT_SHIFT),
563                        // Meta + arrow on (some?) Macs when using iTerm defaults
564                        ('9', UP) => E(K::Up, M::ALT),
565                        ('9', DOWN) => E(K::Down, M::ALT),
566                        ('9', RIGHT) => E(K::Right, M::ALT),
567                        ('9', LEFT) => E(K::Left, M::ALT),
568                        _ => {
569                            debug!(target: "rustyline",
570                                   "unsupported esc sequence: \\E[1;{}{:?}", seq4, seq5);
571                            E(K::UnknownEscSeq, M::NONE)
572                        }
573                    })
574                } else if seq5 == '~' {
575                    Ok(match (seq2, seq4) {
576                        (INSERT, SHIFT) => E(K::Insert, M::SHIFT),
577                        (INSERT, ALT) => E(K::Insert, M::ALT),
578                        (INSERT, ALT_SHIFT) => E(K::Insert, M::ALT_SHIFT),
579                        (INSERT, CTRL) => E(K::Insert, M::CTRL),
580                        (INSERT, CTRL_SHIFT) => E(K::Insert, M::CTRL_SHIFT),
581                        (INSERT, CTRL_ALT) => E(K::Insert, M::CTRL_ALT),
582                        (INSERT, CTRL_ALT_SHIFT) => E(K::Insert, M::CTRL_ALT_SHIFT),
583                        (DELETE, SHIFT) => E(K::Delete, M::SHIFT),
584                        (DELETE, ALT) => E(K::Delete, M::ALT),
585                        (DELETE, ALT_SHIFT) => E(K::Delete, M::ALT_SHIFT),
586                        (DELETE, CTRL) => E(K::Delete, M::CTRL),
587                        (DELETE, CTRL_SHIFT) => E(K::Delete, M::CTRL_SHIFT),
588                        (DELETE, CTRL_ALT) => E(K::Delete, M::CTRL_ALT),
589                        (DELETE, CTRL_ALT_SHIFT) => E(K::Delete, M::CTRL_ALT_SHIFT),
590                        (PAGE_UP, SHIFT) => E(K::PageUp, M::SHIFT),
591                        (PAGE_UP, ALT) => E(K::PageUp, M::ALT),
592                        (PAGE_UP, ALT_SHIFT) => E(K::PageUp, M::ALT_SHIFT),
593                        (PAGE_UP, CTRL) => E(K::PageUp, M::CTRL),
594                        (PAGE_UP, CTRL_SHIFT) => E(K::PageUp, M::CTRL_SHIFT),
595                        (PAGE_UP, CTRL_ALT) => E(K::PageUp, M::CTRL_ALT),
596                        (PAGE_UP, CTRL_ALT_SHIFT) => E(K::PageUp, M::CTRL_ALT_SHIFT),
597                        (PAGE_DOWN, SHIFT) => E(K::PageDown, M::SHIFT),
598                        (PAGE_DOWN, ALT) => E(K::PageDown, M::ALT),
599                        (PAGE_DOWN, ALT_SHIFT) => E(K::PageDown, M::ALT_SHIFT),
600                        (PAGE_DOWN, CTRL) => E(K::PageDown, M::CTRL),
601                        (PAGE_DOWN, CTRL_SHIFT) => E(K::PageDown, M::CTRL_SHIFT),
602                        (PAGE_DOWN, CTRL_ALT) => E(K::PageDown, M::CTRL_ALT),
603                        (PAGE_DOWN, CTRL_ALT_SHIFT) => E(K::PageDown, M::CTRL_ALT_SHIFT),
604                        _ => {
605                            debug!(target: "rustyline",
606                                   "unsupported esc sequence: \\E[{};{:?}~", seq2, seq4);
607                            E(K::UnknownEscSeq, M::NONE)
608                        }
609                    })
610                } else {
611                    debug!(target: "rustyline",
612                           "unsupported esc sequence: \\E[{};{}{:?}", seq2, seq4, seq5);
613                    Ok(E(K::UnknownEscSeq, M::NONE))
614                }
615            } else {
616                debug!(target: "rustyline",
617                       "unsupported esc sequence: \\E[{};{:?}", seq2, seq4);
618                Ok(E(K::UnknownEscSeq, M::NONE))
619            }
620        } else {
621            Ok(match (seq2, seq3) {
622                (DELETE, RXVT_CTRL) => E(K::Delete, M::CTRL),
623                (DELETE, RXVT_CTRL_SHIFT) => E(K::Delete, M::CTRL_SHIFT),
624                (CTRL, UP) => E(K::Up, M::CTRL),
625                (CTRL, DOWN) => E(K::Down, M::CTRL),
626                (CTRL, RIGHT) => E(K::Right, M::CTRL),
627                (CTRL, LEFT) => E(K::Left, M::CTRL),
628                (PAGE_UP, RXVT_CTRL) => E(K::PageUp, M::CTRL),
629                (PAGE_UP, RXVT_SHIFT) => E(K::PageUp, M::SHIFT),
630                (PAGE_UP, RXVT_CTRL_SHIFT) => E(K::PageUp, M::CTRL_SHIFT),
631                (PAGE_DOWN, RXVT_CTRL) => E(K::PageDown, M::CTRL),
632                (PAGE_DOWN, RXVT_SHIFT) => E(K::PageDown, M::SHIFT),
633                (PAGE_DOWN, RXVT_CTRL_SHIFT) => E(K::PageDown, M::CTRL_SHIFT),
634                (RXVT_HOME, RXVT_CTRL) => E(K::Home, M::CTRL),
635                (RXVT_HOME, RXVT_SHIFT) => E(K::Home, M::SHIFT),
636                (RXVT_HOME, RXVT_CTRL_SHIFT) => E(K::Home, M::CTRL_SHIFT),
637                (RXVT_END, RXVT_CTRL) => E(K::End, M::CTRL), // kEND5 or kel
638                (RXVT_END, RXVT_SHIFT) => E(K::End, M::SHIFT),
639                (RXVT_END, RXVT_CTRL_SHIFT) => E(K::End, M::CTRL_SHIFT),
640                _ => {
641                    debug!(target: "rustyline",
642                           "unsupported esc sequence: \\E[{}{:?}", seq2, seq3);
643                    E(K::UnknownEscSeq, M::NONE)
644                }
645            })
646        }
647    }
648
649    /// Handle \EO <seq2> escape sequences
650    fn escape_o(&mut self) -> Result<KeyEvent> {
651        let seq2 = self.next_char()?;
652        Ok(match seq2 {
653            UP => E(K::Up, M::NONE),
654            DOWN => E(K::Down, M::NONE),
655            RIGHT => E(K::Right, M::NONE),
656            LEFT => E(K::Left, M::NONE),
657            //'E' => E(K::, M::),// key_b2, kb2
658            END => E(K::End, M::NONE),   // kend
659            HOME => E(K::Home, M::NONE), // khome
660            'M' => E::ENTER,             // kent
661            'P' => E(K::F(1), M::NONE),  // kf1
662            'Q' => E(K::F(2), M::NONE),  // kf2
663            'R' => E(K::F(3), M::NONE),  // kf3
664            'S' => E(K::F(4), M::NONE),  // kf4
665            'a' => E(K::Up, M::CTRL),
666            'b' => E(K::Down, M::CTRL),
667            'c' => E(K::Right, M::CTRL), // rxvt
668            'd' => E(K::Left, M::CTRL),  // rxvt
669            'l' => E(K::F(8), M::NONE),
670            't' => E(K::F(5), M::NONE),  // kf5 or kb1
671            'u' => E(K::F(6), M::NONE),  // kf6 or kb2
672            'v' => E(K::F(7), M::NONE),  // kf7 or kb3
673            'w' => E(K::F(9), M::NONE),  // kf9 or ka1
674            'x' => E(K::F(10), M::NONE), // kf10 or ka2
675            _ => {
676                debug!(target: "rustyline", "unsupported esc sequence: \\EO{:?}", seq2);
677                E(K::UnknownEscSeq, M::NONE)
678            }
679        })
680    }
681
682    fn poll(&mut self, timeout_ms: i32) -> Result<i32> {
683        let n = self.tty_in.buffer().len();
684        if n > 0 {
685            return Ok(n as i32);
686        }
687        let mut fds = [poll::PollFd::new(self.as_raw_fd(), PollFlags::POLLIN)];
688        let r = poll::poll(&mut fds, timeout_ms);
689        match r {
690            Ok(n) => Ok(n),
691            Err(Errno::EINTR) => {
692                if self.tty_in.get_ref().sigwinch()? {
693                    Err(ReadlineError::WindowResized)
694                } else {
695                    Ok(0) // Ignore EINTR while polling
696                }
697            }
698            Err(e) => Err(e.into()),
699        }
700    }
701
702    fn select(&mut self, single_esc_abort: bool) -> Result<Event> {
703        let tty_in = self.as_raw_fd();
704        let sigwinch_pipe = self.tty_in.get_ref().sigwinch_pipe;
705        let pipe_reader = self
706            .pipe_reader
707            .as_ref()
708            .map(|pr| pr.lock().unwrap().0.as_raw_fd());
709        loop {
710            let mut readfds = self.fds;
711            readfds.clear();
712            if let Some(sigwinch_pipe) = sigwinch_pipe {
713                readfds.insert(sigwinch_pipe);
714            }
715            readfds.insert(tty_in);
716            if let Some(pipe_reader) = pipe_reader {
717                readfds.insert(pipe_reader);
718            }
719            if let Err(err) = select::select(
720                readfds.highest().map(|h| h + 1),
721                Some(&mut readfds),
722                None,
723                None,
724                None,
725            ) {
726                if err == Errno::EINTR && self.tty_in.get_ref().sigwinch()? {
727                    return Err(ReadlineError::WindowResized);
728                } else if err != Errno::EINTR {
729                    return Err(err.into());
730                } else {
731                    continue;
732                }
733            };
734            if sigwinch_pipe.map_or(false, |fd| readfds.contains(fd)) {
735                self.tty_in.get_ref().sigwinch()?;
736                return Err(ReadlineError::WindowResized);
737            } else if readfds.contains(tty_in) {
738                // prefer user input over external print
739                return self.next_key(single_esc_abort).map(Event::KeyPress);
740            } else if let Some(ref pipe_reader) = self.pipe_reader {
741                let mut guard = pipe_reader.lock().unwrap();
742                let mut buf = [0; 1];
743                guard.0.read_exact(&mut buf)?;
744                if let Ok(msg) = guard.1.try_recv() {
745                    return Ok(Event::ExternalPrint(msg));
746                }
747            }
748        }
749    }
750}
751
752impl RawReader for PosixRawReader {
753    #[cfg(not(feature = "signal-hook"))]
754    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
755        match self.pipe_reader {
756            Some(_) => self.select(single_esc_abort),
757            None => self.next_key(single_esc_abort).map(Event::KeyPress),
758        }
759    }
760
761    #[cfg(feature = "signal-hook")]
762    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
763        self.select(single_esc_abort)
764    }
765
766    fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyEvent> {
767        let c = self.next_char()?;
768
769        let mut key = KeyEvent::new(c, M::NONE);
770        if key == E::ESC {
771            if !self.tty_in.buffer().is_empty() {
772                debug!(target: "rustyline", "read buffer {:?}", self.tty_in.buffer());
773            }
774            let timeout_ms = if single_esc_abort && self.timeout_ms == -1 {
775                0
776            } else {
777                self.timeout_ms
778            };
779            match self.poll(timeout_ms) {
780                Ok(n) if n == 0 => {
781                    // single escape
782                }
783                Ok(_) => {
784                    // escape sequence
785                    key = self.escape_sequence()?
786                }
787                // Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
788                Err(e) => return Err(e),
789            }
790        }
791        debug!(target: "rustyline", "c: {:?} => key: {:?}", c, key);
792        Ok(key)
793    }
794
795    fn next_char(&mut self) -> Result<char> {
796        let mut buf = [0; 1];
797        let mut receiver = Utf8 {
798            c: None,
799            valid: true,
800        };
801        loop {
802            let n = self.tty_in.read(&mut buf)?;
803            if n == 0 {
804                return Err(error::ReadlineError::Eof);
805            }
806            let b = buf[0];
807            self.parser.advance(&mut receiver, b);
808            if !receiver.valid {
809                return Err(error::ReadlineError::from(ErrorKind::InvalidData));
810            } else if let Some(c) = receiver.c.take() {
811                return Ok(c);
812            }
813        }
814    }
815
816    fn read_pasted_text(&mut self) -> Result<String> {
817        let mut buffer = String::new();
818        loop {
819            match self.next_char()? {
820                '\x1b' => {
821                    let key = self.escape_sequence()?;
822                    if key == E(K::BracketedPasteEnd, M::NONE) {
823                        break;
824                    } else {
825                        continue; // TODO validate
826                    }
827                }
828                c => buffer.push(c),
829            };
830        }
831        let buffer = buffer.replace("\r\n", "\n");
832        let buffer = buffer.replace('\r', "\n");
833        Ok(buffer)
834    }
835
836    fn find_binding(&self, key: &KeyEvent) -> Option<Cmd> {
837        let cmd = self.key_map.get(key).cloned();
838        if let Some(ref cmd) = cmd {
839            debug!(target: "rustyline", "terminal key binding: {:?} => {:?}", key, cmd);
840        }
841        cmd
842    }
843}
844
845impl Receiver for Utf8 {
846    /// Called whenever a code point is parsed successfully
847    fn codepoint(&mut self, c: char) {
848        self.c = Some(c);
849        self.valid = true;
850    }
851
852    /// Called when an invalid_sequence is detected
853    fn invalid_sequence(&mut self) {
854        self.c = None;
855        self.valid = false;
856    }
857}
858
859/// Console output writer
860pub struct PosixRenderer {
861    out: RawFd,
862    cols: usize, // Number of columns in terminal
863    buffer: String,
864    tab_stop: usize,
865    colors_enabled: bool,
866    bell_style: BellStyle,
867}
868
869impl PosixRenderer {
870    fn new(out: RawFd, tab_stop: usize, colors_enabled: bool, bell_style: BellStyle) -> Self {
871        let (cols, _) = get_win_size(out);
872        Self {
873            out,
874            cols,
875            buffer: String::with_capacity(1024),
876            tab_stop,
877            colors_enabled,
878            bell_style,
879        }
880    }
881
882    fn clear_old_rows(&mut self, layout: &Layout) {
883        use std::fmt::Write;
884        let current_row = layout.cursor.row;
885        let old_rows = layout.end.row;
886        // old_rows < cursor_row if the prompt spans multiple lines and if
887        // this is the default State.
888        let cursor_row_movement = old_rows.saturating_sub(current_row);
889        // move the cursor down as required
890        if cursor_row_movement > 0 {
891            write!(self.buffer, "\x1b[{}B", cursor_row_movement).unwrap();
892        }
893        // clear old rows
894        for _ in 0..old_rows {
895            self.buffer.push_str("\r\x1b[K\x1b[A");
896        }
897        // clear the line
898        self.buffer.push_str("\r\x1b[K");
899    }
900}
901
902impl Renderer for PosixRenderer {
903    type Reader = PosixRawReader;
904
905    fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
906        use std::fmt::Write;
907        self.buffer.clear();
908        let row_ordering = new.row.cmp(&old.row);
909        if row_ordering == cmp::Ordering::Greater {
910            // move down
911            let row_shift = new.row - old.row;
912            if row_shift == 1 {
913                self.buffer.push_str("\x1b[B");
914            } else {
915                write!(self.buffer, "\x1b[{}B", row_shift)?;
916            }
917        } else if row_ordering == cmp::Ordering::Less {
918            // move up
919            let row_shift = old.row - new.row;
920            if row_shift == 1 {
921                self.buffer.push_str("\x1b[A");
922            } else {
923                write!(self.buffer, "\x1b[{}A", row_shift)?;
924            }
925        }
926        let col_ordering = new.col.cmp(&old.col);
927        if col_ordering == cmp::Ordering::Greater {
928            // move right
929            let col_shift = new.col - old.col;
930            if col_shift == 1 {
931                self.buffer.push_str("\x1b[C");
932            } else {
933                write!(self.buffer, "\x1b[{}C", col_shift)?;
934            }
935        } else if col_ordering == cmp::Ordering::Less {
936            // move left
937            let col_shift = old.col - new.col;
938            if col_shift == 1 {
939                self.buffer.push_str("\x1b[D");
940            } else {
941                write!(self.buffer, "\x1b[{}D", col_shift)?;
942            }
943        }
944        write_all(self.out, self.buffer.as_str())?;
945        Ok(())
946    }
947
948    fn refresh_line(
949        &mut self,
950        prompt: &str,
951        line: &LineBuffer,
952        hint: Option<&str>,
953        old_layout: &Layout,
954        new_layout: &Layout,
955        highlighter: Option<&dyn Highlighter>,
956    ) -> Result<()> {
957        use std::fmt::Write;
958        self.buffer.clear();
959
960        let default_prompt = new_layout.default_prompt;
961        let cursor = new_layout.cursor;
962        let end_pos = new_layout.end;
963
964        self.clear_old_rows(old_layout);
965
966        if let Some(highlighter) = highlighter {
967            // display the prompt
968            self.buffer
969                .push_str(&highlighter.highlight_prompt(prompt, default_prompt));
970            // display the input line
971            self.buffer
972                .push_str(&highlighter.highlight(line, line.pos()));
973        } else {
974            // display the prompt
975            self.buffer.push_str(prompt);
976            // display the input line
977            self.buffer.push_str(line);
978        }
979        // display hint
980        if let Some(hint) = hint {
981            if let Some(highlighter) = highlighter {
982                self.buffer.push_str(&highlighter.highlight_hint(hint));
983            } else {
984                self.buffer.push_str(hint);
985            }
986        }
987        // we have to generate our own newline on line wrap
988        if end_pos.col == 0
989            && end_pos.row > 0
990            && !hint
991                .map(|h| h.ends_with('\n'))
992                .unwrap_or_else(|| line.ends_with('\n'))
993        {
994            self.buffer.push('\n');
995        }
996        // position the cursor
997        let new_cursor_row_movement = end_pos.row - cursor.row;
998        // move the cursor up as required
999        if new_cursor_row_movement > 0 {
1000            write!(self.buffer, "\x1b[{}A", new_cursor_row_movement)?;
1001        }
1002        // position the cursor within the line
1003        if cursor.col > 0 {
1004            write!(self.buffer, "\r\x1b[{}C", cursor.col).unwrap();
1005        } else {
1006            self.buffer.push('\r');
1007        }
1008
1009        write_all(self.out, self.buffer.as_str())?;
1010        Ok(())
1011    }
1012
1013    fn write_and_flush(&mut self, buf: &str) -> Result<()> {
1014        write_all(self.out, buf)?;
1015        Ok(())
1016    }
1017
1018    /// Control characters are treated as having zero width.
1019    /// Characters with 2 column width are correctly handled (not split).
1020    fn calculate_position(&self, s: &str, orig: Position) -> Position {
1021        let mut pos = orig;
1022        let mut esc_seq = 0;
1023        for c in s.graphemes(true) {
1024            if c == "\n" {
1025                pos.row += 1;
1026                pos.col = 0;
1027                continue;
1028            }
1029            let cw = if c == "\t" {
1030                self.tab_stop - (pos.col % self.tab_stop)
1031            } else {
1032                width(c, &mut esc_seq)
1033            };
1034            pos.col += cw;
1035            if pos.col > self.cols {
1036                pos.row += 1;
1037                pos.col = cw;
1038            }
1039        }
1040        if pos.col == self.cols {
1041            pos.col = 0;
1042            pos.row += 1;
1043        }
1044        pos
1045    }
1046
1047    fn beep(&mut self) -> Result<()> {
1048        match self.bell_style {
1049            BellStyle::Audible => self.write_and_flush("\x07"),
1050            _ => Ok(()),
1051        }
1052    }
1053
1054    /// Clear the screen. Used to handle ctrl+l
1055    fn clear_screen(&mut self) -> Result<()> {
1056        self.write_and_flush("\x1b[H\x1b[J")
1057    }
1058
1059    fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
1060        self.buffer.clear();
1061        self.clear_old_rows(layout);
1062        write_all(self.out, self.buffer.as_str())?;
1063        Ok(())
1064    }
1065
1066    /// Try to update the number of columns in the current terminal,
1067    fn update_size(&mut self) {
1068        let (cols, _) = get_win_size(self.out);
1069        self.cols = cols;
1070    }
1071
1072    fn get_columns(&self) -> usize {
1073        self.cols
1074    }
1075
1076    /// Try to get the number of rows in the current terminal,
1077    /// or assume 24 if it fails.
1078    fn get_rows(&self) -> usize {
1079        let (_, rows) = get_win_size(self.out);
1080        rows
1081    }
1082
1083    fn colors_enabled(&self) -> bool {
1084        self.colors_enabled
1085    }
1086
1087    fn move_cursor_at_leftmost(&mut self, rdr: &mut PosixRawReader) -> Result<()> {
1088        if rdr.poll(0)? != 0 {
1089            debug!(target: "rustyline", "cannot request cursor location");
1090            return Ok(());
1091        }
1092        /* Report cursor location */
1093        self.write_and_flush("\x1b[6n")?;
1094        /* Read the response: ESC [ rows ; cols R */
1095        if rdr.poll(100)? == 0
1096            || rdr.next_char()? != '\x1b'
1097            || rdr.next_char()? != '['
1098            || read_digits_until(rdr, ';')?.is_none()
1099        {
1100            warn!(target: "rustyline", "cannot read initial cursor location");
1101            return Ok(());
1102        }
1103        let col = read_digits_until(rdr, 'R')?;
1104        debug!(target: "rustyline", "initial cursor location: {:?}", col);
1105        if col != Some(1) {
1106            self.write_and_flush("\n")?;
1107        }
1108        Ok(())
1109    }
1110}
1111
1112fn read_digits_until(rdr: &mut PosixRawReader, sep: char) -> Result<Option<u32>> {
1113    let mut num: u32 = 0;
1114    loop {
1115        match rdr.next_char()? {
1116            digit @ '0'..='9' => {
1117                num = num
1118                    .saturating_mul(10)
1119                    .saturating_add(digit.to_digit(10).unwrap());
1120                continue;
1121            }
1122            c if c == sep => break,
1123            _ => return Ok(None),
1124        }
1125    }
1126    Ok(Some(num))
1127}
1128
1129fn write_all(fd: RawFd, buf: &str) -> nix::Result<()> {
1130    let mut bytes = buf.as_bytes();
1131    while !bytes.is_empty() {
1132        match write(fd, bytes) {
1133            Ok(0) => return Err(Errno::EIO),
1134            Ok(n) => bytes = &bytes[n..],
1135            Err(Errno::EINTR) => {}
1136            Err(r) => return Err(r),
1137        }
1138    }
1139    Ok(())
1140}
1141
1142#[cfg(not(feature = "signal-hook"))]
1143static mut SIGWINCH_PIPE: RawFd = -1;
1144#[cfg(not(feature = "signal-hook"))]
1145extern "C" fn sigwinch_handler(_: libc::c_int) {
1146    let _ = unsafe { write(SIGWINCH_PIPE, &[b's']) };
1147}
1148
1149#[derive(Clone, Debug)]
1150struct SigWinCh {
1151    pipe: RawFd,
1152    #[cfg(not(feature = "signal-hook"))]
1153    original: nix::sys::signal::SigAction,
1154    #[cfg(feature = "signal-hook")]
1155    id: signal_hook::SigId,
1156}
1157impl SigWinCh {
1158    #[cfg(not(feature = "signal-hook"))]
1159    fn install_sigwinch_handler() -> Result<SigWinCh> {
1160        use nix::sys::signal;
1161        let (pipe, pipe_write) = UnixStream::pair()?;
1162        pipe.set_nonblocking(true)?;
1163        unsafe { SIGWINCH_PIPE = pipe_write.into_raw_fd() };
1164        let sigwinch = signal::SigAction::new(
1165            signal::SigHandler::Handler(sigwinch_handler),
1166            signal::SaFlags::empty(),
1167            signal::SigSet::empty(),
1168        );
1169        let original = unsafe { signal::sigaction(signal::SIGWINCH, &sigwinch)? };
1170        Ok(SigWinCh {
1171            pipe: pipe.into_raw_fd(),
1172            original,
1173        })
1174    }
1175
1176    #[cfg(feature = "signal-hook")]
1177    fn install_sigwinch_handler() -> Result<SigWinCh> {
1178        let (pipe, pipe_write) = UnixStream::pair()?;
1179        pipe.set_nonblocking(true)?;
1180        let id = signal_hook::low_level::pipe::register(libc::SIGWINCH, pipe_write)?;
1181        Ok(SigWinCh {
1182            pipe: pipe.into_raw_fd(),
1183            id,
1184        })
1185    }
1186
1187    #[cfg(not(feature = "signal-hook"))]
1188    fn uninstall_sigwinch_handler(self) -> Result<()> {
1189        use nix::sys::signal;
1190        let _ = unsafe { signal::sigaction(signal::SIGWINCH, &self.original)? };
1191        close(self.pipe)?;
1192        unsafe { close(SIGWINCH_PIPE)? };
1193        unsafe { SIGWINCH_PIPE = -1 };
1194        Ok(())
1195    }
1196
1197    #[cfg(feature = "signal-hook")]
1198    fn uninstall_sigwinch_handler(self) -> Result<()> {
1199        signal_hook::low_level::unregister(self.id);
1200        close(self.pipe)?;
1201        Ok(())
1202    }
1203}
1204
1205fn map_key(key_map: &mut HashMap<KeyEvent, Cmd>, raw: &Termios, index: SCI, name: &str, cmd: Cmd) {
1206    let cc = char::from(raw.control_chars[index as usize]);
1207    let key = KeyEvent::new(cc, M::NONE);
1208    debug!(target: "rustyline", "{}: {:?}", name, key);
1209    key_map.insert(key, cmd);
1210}
1211
1212#[cfg(not(test))]
1213pub type Terminal = PosixTerminal;
1214
1215#[derive(Clone, Debug)]
1216pub struct PosixTerminal {
1217    unsupported: bool,
1218    tty_in: RawFd,
1219    is_in_a_tty: bool,
1220    tty_out: RawFd,
1221    is_out_a_tty: bool,
1222    close_on_drop: bool,
1223    pub(crate) color_mode: ColorMode,
1224    tab_stop: usize,
1225    bell_style: BellStyle,
1226    enable_bracketed_paste: bool,
1227    raw_mode: Arc<AtomicBool>,
1228    // external print reader
1229    pipe_reader: Option<PipeReader>,
1230    // external print writer
1231    pipe_writer: Option<PipeWriter>,
1232    sigwinch: Option<SigWinCh>,
1233}
1234
1235impl PosixTerminal {
1236    fn colors_enabled(&self) -> bool {
1237        match self.color_mode {
1238            ColorMode::Enabled => self.is_out_a_tty,
1239            ColorMode::Forced => true,
1240            ColorMode::Disabled => false,
1241        }
1242    }
1243}
1244
1245impl Term for PosixTerminal {
1246    type ExternalPrinter = ExternalPrinter;
1247    type KeyMap = PosixKeyMap;
1248    type Mode = PosixMode;
1249    type Reader = PosixRawReader;
1250    type Writer = PosixRenderer;
1251
1252    fn new(
1253        color_mode: ColorMode,
1254        behavior: Behavior,
1255        tab_stop: usize,
1256        bell_style: BellStyle,
1257        enable_bracketed_paste: bool,
1258    ) -> Result<Self> {
1259        let (tty_in, is_in_a_tty, tty_out, is_out_a_tty, close_on_drop) =
1260            if behavior == Behavior::PreferTerm {
1261                let tty = OpenOptions::new().read(true).write(true).open("/dev/tty");
1262                if let Ok(tty) = tty {
1263                    let fd = tty.into_raw_fd();
1264                    let is_a_tty = is_a_tty(fd); // TODO: useless ?
1265                    (fd, is_a_tty, fd, is_a_tty, true)
1266                } else {
1267                    (
1268                        libc::STDIN_FILENO,
1269                        is_a_tty(libc::STDIN_FILENO),
1270                        libc::STDOUT_FILENO,
1271                        is_a_tty(libc::STDOUT_FILENO),
1272                        false,
1273                    )
1274                }
1275            } else {
1276                (
1277                    libc::STDIN_FILENO,
1278                    is_a_tty(libc::STDIN_FILENO),
1279                    libc::STDOUT_FILENO,
1280                    is_a_tty(libc::STDOUT_FILENO),
1281                    false,
1282                )
1283            };
1284        let unsupported = is_unsupported_term();
1285        #[allow(unused_variables)]
1286        let sigwinch = if !unsupported && is_in_a_tty && is_out_a_tty {
1287            Some(SigWinCh::install_sigwinch_handler()?)
1288        } else {
1289            None
1290        };
1291        Ok(Self {
1292            unsupported,
1293            tty_in,
1294            is_in_a_tty,
1295            tty_out,
1296            is_out_a_tty,
1297            close_on_drop,
1298            color_mode,
1299            tab_stop,
1300            bell_style,
1301            enable_bracketed_paste,
1302            raw_mode: Arc::new(AtomicBool::new(false)),
1303            pipe_reader: None,
1304            pipe_writer: None,
1305            sigwinch,
1306        })
1307    }
1308
1309    // Init checks:
1310
1311    /// Check if current terminal can provide a rich line-editing user
1312    /// interface.
1313    fn is_unsupported(&self) -> bool {
1314        self.unsupported
1315    }
1316
1317    fn is_input_tty(&self) -> bool {
1318        self.is_in_a_tty
1319    }
1320
1321    fn is_output_tty(&self) -> bool {
1322        self.is_out_a_tty
1323    }
1324
1325    // Interactive loop:
1326
1327    fn enable_raw_mode(&mut self) -> Result<(Self::Mode, PosixKeyMap)> {
1328        use nix::errno::Errno::ENOTTY;
1329        use nix::sys::termios::{ControlFlags, InputFlags, LocalFlags};
1330        if !self.is_in_a_tty {
1331            return Err(ENOTTY.into());
1332        }
1333        let original_mode = termios::tcgetattr(self.tty_in)?;
1334        let mut raw = original_mode.clone();
1335        // disable BREAK interrupt, CR to NL conversion on input,
1336        // input parity check, strip high bit (bit 8), output flow control
1337        raw.input_flags &= !(InputFlags::BRKINT
1338            | InputFlags::ICRNL
1339            | InputFlags::INPCK
1340            | InputFlags::ISTRIP
1341            | InputFlags::IXON);
1342        // we don't want raw output, it turns newlines into straight line feeds
1343        // disable all output processing
1344        // raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);
1345
1346        // character-size mark (8 bits)
1347        raw.control_flags |= ControlFlags::CS8;
1348        // disable echoing, canonical mode, extended input processing and signals
1349        raw.local_flags &=
1350            !(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG);
1351        raw.control_chars[SCI::VMIN as usize] = 1; // One character-at-a-time input
1352        raw.control_chars[SCI::VTIME as usize] = 0; // with blocking read
1353
1354        let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
1355        map_key(&mut key_map, &raw, SCI::VEOF, "VEOF", Cmd::EndOfFile);
1356        map_key(&mut key_map, &raw, SCI::VINTR, "VINTR", Cmd::Interrupt);
1357        map_key(&mut key_map, &raw, SCI::VQUIT, "VQUIT", Cmd::Interrupt);
1358        map_key(&mut key_map, &raw, SCI::VSUSP, "VSUSP", Cmd::Suspend);
1359
1360        termios::tcsetattr(self.tty_in, SetArg::TCSADRAIN, &raw)?;
1361
1362        self.raw_mode.store(true, Ordering::SeqCst);
1363        // enable bracketed paste
1364        let out = if !self.enable_bracketed_paste {
1365            None
1366        } else if let Err(e) = write_all(self.tty_out, BRACKETED_PASTE_ON) {
1367            debug!(target: "rustyline", "Cannot enable bracketed paste: {}", e);
1368            None
1369        } else {
1370            Some(self.tty_out)
1371        };
1372
1373        // when all ExternalPrinter are dropped there is no need to use `pipe_reader`
1374        if Arc::strong_count(&self.raw_mode) == 1 {
1375            self.pipe_writer = None;
1376            self.pipe_reader = None;
1377        }
1378
1379        Ok((
1380            PosixMode {
1381                termios: original_mode,
1382                tty_in: self.tty_in,
1383                tty_out: out,
1384                raw_mode: self.raw_mode.clone(),
1385            },
1386            key_map,
1387        ))
1388    }
1389
1390    /// Create a RAW reader
1391    fn create_reader(&self, config: &Config, key_map: PosixKeyMap) -> PosixRawReader {
1392        PosixRawReader::new(
1393            self.tty_in,
1394            self.sigwinch.as_ref().map(|s| s.pipe),
1395            config,
1396            key_map,
1397            self.pipe_reader.clone(),
1398        )
1399    }
1400
1401    fn create_writer(&self) -> PosixRenderer {
1402        PosixRenderer::new(
1403            self.tty_out,
1404            self.tab_stop,
1405            self.colors_enabled(),
1406            self.bell_style,
1407        )
1408    }
1409
1410    fn writeln(&self) -> Result<()> {
1411        write_all(self.tty_out, "\n")?;
1412        Ok(())
1413    }
1414
1415    fn create_external_printer(&mut self) -> Result<ExternalPrinter> {
1416        if let Some(ref writer) = self.pipe_writer {
1417            return Ok(ExternalPrinter {
1418                writer: writer.clone(),
1419                raw_mode: self.raw_mode.clone(),
1420                tty_out: self.tty_out,
1421            });
1422        }
1423        if self.unsupported || !self.is_input_tty() || !self.is_output_tty() {
1424            return Err(nix::Error::ENOTTY.into());
1425        }
1426        use nix::unistd::pipe;
1427        use std::os::unix::io::FromRawFd;
1428        let (sender, receiver) = mpsc::sync_channel(1); // TODO validate: bound
1429        let (r, w) = pipe()?;
1430        let reader = Arc::new(Mutex::new((unsafe { File::from_raw_fd(r) }, receiver)));
1431        let writer = (
1432            Arc::new(Mutex::new(unsafe { File::from_raw_fd(w) })),
1433            sender,
1434        );
1435        self.pipe_reader.replace(reader);
1436        self.pipe_writer.replace(writer.clone());
1437        Ok(ExternalPrinter {
1438            writer,
1439            raw_mode: self.raw_mode.clone(),
1440            tty_out: self.tty_out,
1441        })
1442    }
1443}
1444
1445#[allow(unused_must_use)]
1446impl Drop for PosixTerminal {
1447    fn drop(&mut self) {
1448        if self.close_on_drop {
1449            close(self.tty_in);
1450            debug_assert_eq!(self.tty_in, self.tty_out);
1451        }
1452        if let Some(sigwinch) = self.sigwinch.take() {
1453            sigwinch.uninstall_sigwinch_handler();
1454        }
1455    }
1456}
1457
1458#[derive(Debug)]
1459pub struct ExternalPrinter {
1460    writer: PipeWriter,
1461    raw_mode: Arc<AtomicBool>,
1462    tty_out: RawFd,
1463}
1464
1465impl super::ExternalPrinter for ExternalPrinter {
1466    fn print(&mut self, msg: String) -> Result<()> {
1467        // write directly to stdout/stderr while not in raw mode
1468        if !self.raw_mode.load(Ordering::SeqCst) {
1469            write_all(self.tty_out, msg.as_str())?;
1470        } else if let Ok(mut writer) = self.writer.0.lock() {
1471            self.writer
1472                .1
1473                .send(msg)
1474                .map_err(|_| io::Error::from(ErrorKind::Other))?; // FIXME
1475            writer.write_all(&[b'm'])?;
1476            writer.flush()?;
1477        } else {
1478            return Err(io::Error::from(ErrorKind::Other).into()); // FIXME
1479        }
1480        Ok(())
1481    }
1482}
1483
1484#[cfg(not(test))]
1485pub fn suspend() -> Result<()> {
1486    use nix::sys::signal;
1487    use nix::unistd::Pid;
1488    // suspend the whole process group
1489    signal::kill(Pid::from_raw(0), signal::SIGTSTP)?;
1490    Ok(())
1491}
1492
1493#[cfg(test)]
1494mod test {
1495    use super::{Position, PosixRenderer, PosixTerminal, Renderer};
1496    use crate::config::BellStyle;
1497    use crate::line_buffer::LineBuffer;
1498
1499    #[test]
1500    #[ignore]
1501    fn prompt_with_ansi_escape_codes() {
1502        let out = PosixRenderer::new(libc::STDOUT_FILENO, 4, true, BellStyle::default());
1503        let pos = out.calculate_position("\x1b[1;32m>>\x1b[0m ", Position::default());
1504        assert_eq!(3, pos.col);
1505        assert_eq!(0, pos.row);
1506    }
1507
1508    #[test]
1509    fn test_unsupported_term() {
1510        std::env::set_var("TERM", "xterm");
1511        assert!(!super::is_unsupported_term());
1512
1513        std::env::set_var("TERM", "dumb");
1514        assert!(super::is_unsupported_term());
1515    }
1516
1517    #[test]
1518    fn test_send() {
1519        fn assert_send<T: Send>() {}
1520        assert_send::<PosixTerminal>();
1521    }
1522
1523    #[test]
1524    fn test_sync() {
1525        fn assert_sync<T: Sync>() {}
1526        assert_sync::<PosixTerminal>();
1527    }
1528
1529    #[test]
1530    fn test_line_wrap() {
1531        let mut out = PosixRenderer::new(libc::STDOUT_FILENO, 4, true, BellStyle::default());
1532        let prompt = "> ";
1533        let default_prompt = true;
1534        let prompt_size = out.calculate_position(prompt, Position::default());
1535
1536        let mut line = LineBuffer::init("", 0, None);
1537        let old_layout = out.compute_layout(prompt_size, default_prompt, &line, None);
1538        assert_eq!(Position { col: 2, row: 0 }, old_layout.cursor);
1539        assert_eq!(old_layout.cursor, old_layout.end);
1540
1541        assert_eq!(Some(true), line.insert('a', out.cols - prompt_size.col + 1));
1542        let new_layout = out.compute_layout(prompt_size, default_prompt, &line, None);
1543        assert_eq!(Position { col: 1, row: 1 }, new_layout.cursor);
1544        assert_eq!(new_layout.cursor, new_layout.end);
1545        out.refresh_line(prompt, &line, None, &old_layout, &new_layout, None)
1546            .unwrap();
1547        #[rustfmt::skip]
1548        assert_eq!(
1549            "\r\u{1b}[K> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\u{1b}[1C",
1550            out.buffer
1551        );
1552    }
1553}