1use 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
29const 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 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
70fn 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
86fn 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 fn disable_raw_mode(&self) -> Result<()> {
109 termios::tcsetattr(self.tty_in, SetArg::TCSADRAIN, &self.termios)?;
110 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
119struct 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 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
171type PipeReader = Arc<Mutex<(File, mpsc::Receiver<String>)>>;
173type PipeWriter = (Arc<Mutex<File>>, SyncSender<String>);
175
176pub struct PosixRawReader {
178 tty_in: BufReader<TtyIn>,
179 timeout_ms: i32,
180 parser: Parser,
181 key_map: PosixKeyMap,
182 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'; const DOWN: char = 'B'; const RIGHT: char = 'C'; const LEFT: char = 'D'; const END: char = 'F'; const HOME: char = 'H'; const INSERT: char = '2'; const DELETE: char = '3'; const PAGE_UP: char = '5'; const PAGE_DOWN: char = '6'; const RXVT_HOME: char = '7';
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 fn escape_sequence(&mut self) -> Result<KeyEvent> {
245 self._do_escape_sequence(true)
246 }
247
248 fn _do_escape_sequence(&mut self, allow_recurse: bool) -> Result<KeyEvent> {
250 let seq1 = self.next_char()?;
252 if seq1 == '[' {
253 self.escape_csi()
255 } else if seq1 == 'O' {
256 self.escape_o()
259 } else if seq1 == '\x1b' {
260 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 Ok(0) | Err(_) => Ok(E::ESC),
287 Ok(n) => {
288 debug_assert!(n > 0, "{}", n);
289 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 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 self.extended_escape(seq2)
311 }
312 }
313 } else if seq2 == '[' {
314 let seq3 = self.next_char()?;
315 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 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 END => E(K::End, M::NONE),
336 HOME => E(K::Home, M::NONE), 'Z' => E(K::BackTab, M::NONE),
344 'a' => E(K::Up, M::SHIFT), 'b' => E(K::Down, M::SHIFT), 'c' => E(K::Right, M::SHIFT), 'd' => E(K::Left, M::SHIFT), _ => {
349 debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2);
350 E(K::UnknownEscSeq, M::NONE)
351 }
352 })
353 }
354 }
355
356 #[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), INSERT => E(K::Insert, M::NONE),
364 DELETE => E(K::Delete, M::NONE),
365 '4' | RXVT_END => E(K::End, M::NONE), 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), ('1', '2') => E(K::F(2), M::NONE), ('1', '3') => E(K::F(3), M::NONE), ('1', '4') => E(K::F(4), M::NONE), ('1', '5') => E(K::F(5), M::NONE), ('1', '7') => E(K::F(6), M::NONE), ('1', '8') => E(K::F(7), M::NONE), ('1', '9') => E(K::F(8), M::NONE), ('2', '0') => E(K::F(9), M::NONE), ('2', '1') => E(K::F(10), M::NONE), ('2', '3') => E(K::F(11), M::NONE), ('2', '4') => E(K::F(12), M::NONE), _ => {
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()?; 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', '7', CTRL) => E(K::F(6), M::CTRL),
412 ('1', '8', CTRL) => E(K::F(7), M::CTRL),
414 ('1', '9', CTRL) => E(K::F(8), M::CTRL),
415 ('2', '0', CTRL) => E(K::F(9), M::CTRL),
417 ('2', '1', CTRL) => E(K::F(10), M::CTRL),
419 ('2', '3', CTRL) => E(K::F(11), M::CTRL),
421 ('2', '4', CTRL) => E(K::F(12), M::CTRL),
423 _ => {
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()?; Ok(E(K::UnknownEscSeq, M::NONE))
470 } else if seq2 == '1' {
471 Ok(match (seq4, seq5) {
472 (SHIFT, UP) => E(K::Up, M::SHIFT), (SHIFT, DOWN) => E(K::Down, M::SHIFT), (SHIFT, RIGHT) => E(K::Right, M::SHIFT),
475 (SHIFT, LEFT) => E(K::Left, M::SHIFT),
476 (SHIFT, END) => E(K::End, M::SHIFT), (SHIFT, HOME) => E(K::Home, M::SHIFT), (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 (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 ('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), (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 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 END => E(K::End, M::NONE), HOME => E(K::Home, M::NONE), 'M' => E::ENTER, 'P' => E(K::F(1), M::NONE), 'Q' => E(K::F(2), M::NONE), 'R' => E(K::F(3), M::NONE), 'S' => E(K::F(4), M::NONE), 'a' => E(K::Up, M::CTRL),
666 'b' => E(K::Down, M::CTRL),
667 'c' => E(K::Right, M::CTRL), 'd' => E(K::Left, M::CTRL), 'l' => E(K::F(8), M::NONE),
670 't' => E(K::F(5), M::NONE), 'u' => E(K::F(6), M::NONE), 'v' => E(K::F(7), M::NONE), 'w' => E(K::F(9), M::NONE), 'x' => E(K::F(10), M::NONE), _ => {
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) }
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 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 }
783 Ok(_) => {
784 key = self.escape_sequence()?
786 }
787 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; }
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 fn codepoint(&mut self, c: char) {
848 self.c = Some(c);
849 self.valid = true;
850 }
851
852 fn invalid_sequence(&mut self) {
854 self.c = None;
855 self.valid = false;
856 }
857}
858
859pub struct PosixRenderer {
861 out: RawFd,
862 cols: usize, 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 let cursor_row_movement = old_rows.saturating_sub(current_row);
889 if cursor_row_movement > 0 {
891 write!(self.buffer, "\x1b[{}B", cursor_row_movement).unwrap();
892 }
893 for _ in 0..old_rows {
895 self.buffer.push_str("\r\x1b[K\x1b[A");
896 }
897 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 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 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 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 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 self.buffer
969 .push_str(&highlighter.highlight_prompt(prompt, default_prompt));
970 self.buffer
972 .push_str(&highlighter.highlight(line, line.pos()));
973 } else {
974 self.buffer.push_str(prompt);
976 self.buffer.push_str(line);
978 }
979 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 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 let new_cursor_row_movement = end_pos.row - cursor.row;
998 if new_cursor_row_movement > 0 {
1000 write!(self.buffer, "\x1b[{}A", new_cursor_row_movement)?;
1001 }
1002 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 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 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 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 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 self.write_and_flush("\x1b[6n")?;
1094 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 pipe_reader: Option<PipeReader>,
1230 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); (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 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 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 raw.input_flags &= !(InputFlags::BRKINT
1338 | InputFlags::ICRNL
1339 | InputFlags::INPCK
1340 | InputFlags::ISTRIP
1341 | InputFlags::IXON);
1342 raw.control_flags |= ControlFlags::CS8;
1348 raw.local_flags &=
1350 !(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG);
1351 raw.control_chars[SCI::VMIN as usize] = 1; raw.control_chars[SCI::VTIME as usize] = 0; let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(4);
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 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 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 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); 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 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))?; writer.write_all(&[b'm'])?;
1476 writer.flush()?;
1477 } else {
1478 return Err(io::Error::from(ErrorKind::Other).into()); }
1480 Ok(())
1481 }
1482}
1483
1484#[cfg(not(test))]
1485pub fn suspend() -> Result<()> {
1486 use nix::sys::signal;
1487 use nix::unistd::Pid;
1488 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}