1use derivative::Derivative;
6use starnix_uapi::errors::Errno;
7use starnix_uapi::signals::{SIGINT, SIGQUIT, SIGTSTP, Signal};
8use starnix_uapi::vfs::FdEvents;
9use starnix_uapi::{
10 CREAD, CS8, CSIZE, ECHO, ECHOCTL, ECHOE, ECHOK, ECHOKE, ECHONL, ECHOPRT, EXTPROC, ICANON,
11 ICRNL, IEXTEN, IGNCR, INLCR, ISIG, ISTRIP, IUCLC, IUTF8, IXANY, IXON, NOFLSH, OCRNL, OLCUC,
12 ONLCR, ONLRET, ONOCR, OPOST, PARENB, PARMRK, TABDLY, VEOF, VEOL, VEOL2, VERASE, VINTR, VKILL,
13 VLNEXT, VQUIT, VREPRINT, VSTART, VSTOP, VSUSP, VWERASE, XTABS, cc_t, error, tcflag_t, uapi,
14};
15use std::collections::VecDeque;
16
17const CANON_MAX_BYTES: usize = 4096;
20
21const NON_CANON_MAX_BYTES: usize = CANON_MAX_BYTES - 1;
24
25const WAIT_BUFFER_MAX_BYTES: usize = 131072;
28
29const SPACES_PER_TAB: usize = 8;
30
31const DISABLED_CHAR: u8 = 0;
33
34const BACKSPACE_CHAR: u8 = 8; const CONTROL_OFFSET: u8 = 0x40;
40
41#[derive(Derivative)]
42#[derivative(Default)]
43#[derivative(Debug)]
44pub struct LineDiscipline {
45 #[derivative(Default(value = "true"))]
47 pub locked: bool,
48
49 #[derivative(Default(value = "false"))]
51 pub stopped: bool,
52
53 pub window_size: uapi::winsize,
55
56 #[derivative(Default(value = "get_default_termios()"))]
58 termios: uapi::termios2,
59
60 #[derivative(Default(value = "false"))]
62 erasing: bool,
63
64 #[derivative(Default(value = "false"))]
66 lnext: bool,
67
68 column: usize,
71
72 canon_column: usize,
74
75 pending_echoes: Vec<EchoOp>,
77
78 #[derivative(Default(value = "false"))]
80 packet_mode_enabled: bool,
81
82 #[derivative(Default(value = "0"))]
84 packet_mode_pending_events: u8,
85
86 main_references: Option<u32>,
89
90 replica_references: Option<u32>,
93
94 #[derivative(Default(value = "Queue::input_queue()"))]
96 input_queue: Option<Queue>,
97
98 #[derivative(Default(value = "Queue::output_queue()"))]
100 output_queue: Option<Queue>,
101}
102
103pub trait InputBuffer {
105 fn available(&self) -> usize;
106 fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno>;
107}
108
109pub trait OutputBuffer {
110 fn available(&self) -> usize;
111 fn write(&mut self, data: &[u8]) -> Result<usize, Errno>;
112}
113
114macro_rules! with_queue {
116 ($self_:tt . $name:ident . $fn:ident ( $($param:expr),*$(,)?)) => {
117 {
118 let mut queue = $self_.$name . take().unwrap();
119 let result = queue.$fn( $($param),* );
120 $self_.$name = Some(queue);
121 result
122 }
123 };
124}
125
126#[derive(Debug, PartialEq)]
128#[must_use]
129pub struct PendingSignals {
130 signals: Vec<Signal>,
131}
132
133impl PendingSignals {
134 pub fn new() -> Self {
135 Self { signals: vec![] }
136 }
137
138 fn add(&mut self, signal: Signal) {
140 self.signals.push(signal);
141 }
142
143 fn append(&mut self, mut other: Self) {
145 self.signals.append(&mut other.signals);
146 }
147
148 pub fn signals(&self) -> &[Signal] {
150 &self.signals[..]
151 }
152}
153
154#[derive(Debug, PartialEq)]
156enum EraseType {
157 Character,
159 Word,
161 Line,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum TerminalSide {
168 Main,
169 Replica,
170}
171
172impl From<bool> for TerminalSide {
173 fn from(is_main: bool) -> Self {
174 if is_main { Self::Main } else { Self::Replica }
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq)]
181enum EchoOp {
182 Byte(RawByte),
184 SetCanonColumn,
186 EraseTab { num_chars: usize, after_tab: bool },
189}
190
191impl LineDiscipline {
192 pub fn termios(&self) -> &uapi::termios2 {
194 &self.termios
195 }
196
197 fn is_canon_enabled(&self) -> bool {
198 self.termios.has_local_flags(ICANON) && !self.is_extproc_enabled()
199 }
200
201 fn is_extproc_enabled(&self) -> bool {
202 self.termios.has_local_flags(EXTPROC)
203 }
204
205 pub fn is_packet_mode_enabled(&self) -> bool {
206 self.packet_mode_enabled
207 }
208
209 pub fn set_packet_mode(&mut self, enabled: bool) {
210 self.packet_mode_enabled = enabled;
211 if !enabled {
212 self.packet_mode_pending_events = 0;
213 }
214 }
215
216 pub fn get_available_read_size(&self, side: TerminalSide) -> usize {
218 let queue = match side {
219 TerminalSide::Main => self.output_queue(),
220 TerminalSide::Replica => self.input_queue(),
221 };
222 queue.readable_size()
223 }
224
225 fn reset_input_state(&mut self) {
227 self.erasing = false;
228 self.lnext = false;
229 self.pending_echoes.clear();
230 }
231
232 pub fn set_termios(&mut self, mut termios: uapi::termios2) -> PendingSignals {
234 termios.c_cflag &= !(CSIZE | PARENB);
236 termios.c_cflag |= CS8 | CREAD;
237
238 let old_ixon = self.termios.has_input_flags(IXON);
239 let new_ixon = termios.has_input_flags(IXON);
240 let old_flow = old_ixon
241 && self.termios.c_cc[VSTOP as usize] == get_control_character('S')
242 && self.termios.c_cc[VSTART as usize] == get_control_character('Q');
243 let new_flow = new_ixon
244 && termios.c_cc[VSTOP as usize] == get_control_character('S')
245 && termios.c_cc[VSTART as usize] == get_control_character('Q');
246 let extproc_active = ((self.termios.c_lflag | termios.c_lflag) & EXTPROC) != 0;
247 let canon_or_extproc_changed =
248 ((self.termios.c_lflag ^ termios.c_lflag) & (ICANON | EXTPROC)) != 0;
249
250 self.termios = termios;
251
252 if self.packet_mode_enabled {
253 if old_flow != new_flow {
254 self.packet_mode_pending_events &=
255 !((uapi::TIOCPKT_DOSTOP | uapi::TIOCPKT_NOSTOP) as u8);
256 let event = if new_flow { uapi::TIOCPKT_DOSTOP } else { uapi::TIOCPKT_NOSTOP };
257 self.packet_mode_pending_events |= event as u8;
258 }
259 if extproc_active {
260 self.packet_mode_pending_events |= uapi::TIOCPKT_IOCTL as u8;
261 }
262 }
263
264 if old_ixon && !new_ixon {
265 self.start_tty();
266 }
267
268 if canon_or_extproc_changed {
269 self.erasing = false;
270 self.lnext = false;
271 with_queue!(self.input_queue.on_canon_mode_changed(self))
272 } else {
273 PendingSignals::new()
274 }
275 }
276
277 pub fn flush(&mut self, side: TerminalSide, queue_selector: u32) -> Result<(), Errno> {
279 let flush_terminal_input = side == TerminalSide::Replica
280 && matches!(queue_selector, uapi::TCIFLUSH | uapi::TCIOFLUSH);
281
282 let (input_queue, output_queue) = match side {
285 TerminalSide::Main => {
286 (self.output_queue.as_mut().unwrap(), self.input_queue.as_mut().unwrap())
287 }
288 TerminalSide::Replica => {
289 (self.input_queue.as_mut().unwrap(), self.output_queue.as_mut().unwrap())
290 }
291 };
292
293 let event;
294 match queue_selector {
302 uapi::TCIFLUSH => {
303 input_queue.flush();
304 event = uapi::TIOCPKT_FLUSHREAD;
305 }
306 uapi::TCOFLUSH => {
307 output_queue.flush_unprocessed();
308 event = uapi::TIOCPKT_FLUSHWRITE;
309 }
310 uapi::TCIOFLUSH => {
311 input_queue.flush();
312 output_queue.flush_unprocessed();
313 event = uapi::TIOCPKT_FLUSHREAD | uapi::TIOCPKT_FLUSHWRITE;
314 }
315 _ => return error!(EINVAL),
316 };
317
318 if flush_terminal_input {
319 self.reset_input_state();
320 }
321
322 if side == TerminalSide::Replica && self.packet_mode_enabled {
323 self.packet_mode_pending_events |= event as u8;
324 }
325
326 Ok(())
327 }
328
329 pub fn main_close(&mut self) {
331 self.main_references = self.main_references.map(|v| v - 1);
332 if self.is_main_closed() {
333 let _ = self.flush(TerminalSide::Replica, uapi::TCIFLUSH);
334 let _ = self.flush(TerminalSide::Main, uapi::TCIFLUSH);
335 }
336 }
337
338 pub fn main_open(&mut self) {
340 self.main_references = Some(self.main_references.unwrap_or(0) + 1);
341 }
342
343 pub fn is_main_closed(&self) -> bool {
344 matches!(self.main_references, Some(0))
345 }
346
347 pub fn main_query_events(&self) -> FdEvents {
349 let mut events =
350 self.output_queue().read_readiness() | self.input_queue().write_readiness();
351 if self.packet_mode_enabled && self.packet_mode_pending_events != 0 {
352 events |= FdEvents::POLLIN | FdEvents::POLLPRI;
353 }
354 if self.is_replica_closed() {
355 events |= FdEvents::POLLHUP;
356 }
357 events
358 }
359
360 pub fn main_read(&mut self, data: &mut dyn OutputBuffer) -> Result<usize, Errno> {
362 if self.packet_mode_enabled && self.packet_mode_pending_events != 0 {
363 if data.available() == 0 {
364 return Ok(0);
365 }
366 let event = self.packet_mode_pending_events;
367 let written = data.write(&[event])?;
368 if written > 0 {
369 self.packet_mode_pending_events = 0;
370 }
371 return Ok(written);
372 }
373 if self.is_replica_closed() && self.output_queue().readable_size() == 0 {
374 return error!(EIO);
375 }
376 if self.packet_mode_enabled {
377 if self.output_queue().readable_size() == 0 {
378 return error!(EAGAIN);
379 }
380 if data.available() == 0 {
381 return Ok(0);
382 }
383 let written = data.write(&[0])?;
384 if written == 0 {
385 return Ok(0);
386 }
387 let res = with_queue!(self.output_queue.read(self, data));
388 match res {
389 Ok((n, signals)) => {
390 assert!(signals.signals().is_empty());
391 return Ok(n + 1);
392 }
393 Err(_) => return Ok(1),
394 }
395 }
396 let (n, signals) = with_queue!(self.output_queue.read(self, data))?;
397 assert!(signals.signals().is_empty());
398 Ok(n)
399 }
400
401 pub fn main_write(
405 &mut self,
406 data: &mut dyn InputBuffer,
407 ) -> Result<(usize, PendingSignals), Errno> {
408 with_queue!(self.input_queue.write(self, data))
409 }
410
411 pub fn replica_close(&mut self) {
413 self.replica_references = self.replica_references.map(|v| v - 1);
414 }
415
416 pub fn replica_open(&mut self) {
418 self.replica_references = Some(self.replica_references.unwrap_or(0) + 1);
419 }
420
421 pub fn is_replica_closed(&self) -> bool {
422 matches!(self.replica_references, Some(0))
423 }
424
425 pub fn replica_query_events(&self) -> FdEvents {
427 if self.is_main_closed() {
428 return FdEvents::POLLIN | FdEvents::POLLOUT | FdEvents::POLLERR | FdEvents::POLLHUP;
429 }
430 let mut events = self.input_queue().read_readiness();
431 if !(self.stopped && self.termios.has_input_flags(IXON)) {
432 events |= self.output_queue().write_readiness();
433 }
434 events
435 }
436
437 pub fn replica_read(
442 &mut self,
443 data: &mut dyn OutputBuffer,
444 ) -> Result<(usize, PendingSignals), Errno> {
445 if self.is_main_closed() {
446 return Ok((0, PendingSignals::new()));
447 }
448 with_queue!(self.input_queue.read(self, data))
449 }
450
451 pub fn replica_write(&mut self, data: &mut dyn InputBuffer) -> Result<usize, Errno> {
453 if self.is_main_closed() {
454 return error!(EIO);
455 }
456 if self.stopped && self.termios.has_input_flags(IXON) {
457 return error!(EAGAIN);
458 }
459 let (read_from_userspace, signals) = with_queue!(self.output_queue.write(self, data))?;
460 assert!(signals.signals().is_empty());
462 Ok(read_from_userspace)
463 }
464
465 fn input_queue(&self) -> &Queue {
467 self.input_queue.as_ref().unwrap()
468 }
469
470 fn output_queue(&self) -> &Queue {
472 self.output_queue.as_ref().unwrap()
473 }
474
475 fn handle_signals(&mut self, byte: RawByte) -> Option<Signal> {
477 if !self.termios.has_local_flags(ISIG) {
478 return None;
479 }
480 self.termios.signal(byte)
481 }
482
483 fn stop_tty(&mut self) {
484 if !self.stopped {
485 self.stopped = true;
486 if self.packet_mode_enabled {
487 self.packet_mode_pending_events &= !(uapi::TIOCPKT_START as u8);
488 self.packet_mode_pending_events |= uapi::TIOCPKT_STOP as u8;
489 }
490 }
491 }
492
493 fn start_tty(&mut self) {
494 if self.stopped {
495 self.stopped = false;
496 if self.packet_mode_enabled {
497 self.packet_mode_pending_events &= !(uapi::TIOCPKT_STOP as u8);
498 self.packet_mode_pending_events |= uapi::TIOCPKT_START as u8;
499 }
500 self.commit_echoes();
501 let signals = with_queue!(self.output_queue.drain_waiting_buffer(self));
502 assert!(signals.signals().is_empty());
503 }
504 }
505
506 fn do_output_char(&mut self, mut c: RawByte, out: &mut Vec<RawByte>) {
507 if !self.termios.has_output_flags(OPOST) {
508 out.push(c);
509 return;
510 }
511 match c {
512 b'\n' => {
513 if self.termios.has_output_flags(ONLRET) {
514 self.column = 0;
515 }
516 if self.termios.has_output_flags(ONLCR) {
517 self.canon_column = 0;
518 self.column = 0;
519 out.extend_from_slice(b"\r\n");
520 return;
521 }
522 self.canon_column = self.column;
523 }
524 b'\r' => {
525 if self.termios.has_output_flags(ONOCR) && self.column == 0 {
526 return;
527 }
528 if self.termios.has_output_flags(OCRNL) {
529 c = b'\n';
530 if self.termios.has_output_flags(ONLRET) {
531 self.canon_column = 0;
532 self.column = 0;
533 }
534 } else {
535 self.canon_column = 0;
536 self.column = 0;
537 }
538 }
539 b'\t' => {
540 let spaces = SPACES_PER_TAB - (self.column % SPACES_PER_TAB);
541 self.column += spaces;
542 if self.termios.c_oflag & TABDLY == XTABS {
543 out.extend(std::iter::repeat_n(b' ', spaces));
544 return;
545 }
546 }
547 BACKSPACE_CHAR => {
548 if self.column > 0 {
549 self.column -= 1;
550 }
551 }
552 _ => {
553 if !is_cntrl(c) {
554 if self.termios.has_output_flags(OLCUC) {
555 c.make_ascii_uppercase();
556 }
557 if !is_utf8_continuation(c, &self.termios) {
558 self.column += 1;
559 }
560 }
561 }
562 }
563 out.push(c);
564 }
565
566 fn echo_raw_byte(&mut self, c: RawByte) {
567 self.pending_echoes.push(EchoOp::Byte(c));
568 }
569
570 fn echo_set_canon_col(&mut self) {
571 self.pending_echoes.push(EchoOp::SetCanonColumn);
572 }
573
574 fn echo_char(&mut self, c: RawByte) {
575 if self.termios.has_local_flags(ECHOCTL) && is_cntrl(c) && c != b'\t' {
576 self.echo_raw_byte(b'^');
577 self.echo_raw_byte(c ^ CONTROL_OFFSET);
578 } else {
579 self.echo_raw_byte(c);
580 }
581 }
582
583 fn finish_erasing(&mut self) {
584 if self.erasing {
585 self.echo_raw_byte(b'/');
586 self.erasing = false;
587 }
588 }
589
590 fn commit_echoes(&mut self) {
591 if self.stopped && self.termios.has_input_flags(IXON) {
592 return;
593 }
594 if !self.pending_echoes.is_empty() {
595 let ops = std::mem::take(&mut self.pending_echoes);
596 let mut echoes = Vec::with_capacity(ops.len());
597 for op in ops {
598 match op {
599 EchoOp::Byte(c) => self.do_output_char(c, &mut echoes),
600 EchoOp::SetCanonColumn => {
601 self.canon_column = self.column;
602 }
603 EchoOp::EraseTab { mut num_chars, after_tab } => {
604 if !after_tab {
605 num_chars += self.canon_column;
606 }
607 let num_bs = SPACES_PER_TAB - (num_chars % SPACES_PER_TAB);
608 for _ in 0..num_bs {
609 self.do_output_char(BACKSPACE_CHAR, &mut echoes);
610 }
611 }
612 }
613 }
614 if !echoes.is_empty() {
615 if let Some(ref mut output_queue) = self.output_queue {
616 output_queue.read_queue.push_back(ReadPacket { data: echoes, has_eof: false });
617 }
618 }
619 }
620 }
621
622 fn eraser(&mut self, queue: &mut Queue, c: RawByte) {
623 if queue.line_buffer.is_empty() {
624 return;
625 }
626
627 let erase_type = if self.termios.is_erase(c) {
628 EraseType::Character
629 } else if self.termios.is_werase(c) {
630 EraseType::Word
631 } else {
632 if !self.termios.has_local_flags(ECHO) {
633 queue.line_buffer.clear();
634 return;
635 }
636 if !self.termios.has_local_flags(ECHOK)
637 || !self.termios.has_local_flags(ECHOKE)
638 || !self.termios.has_local_flags(ECHOE)
639 {
640 queue.line_buffer.clear();
641 self.finish_erasing();
642 self.echo_char(c);
643 if self.termios.has_local_flags(ECHOK) {
644 self.echo_raw_byte(b'\n');
645 }
646 return;
647 }
648 EraseType::Line
649 };
650
651 let mut seen_alnums = 0;
652 while !queue.line_buffer.is_empty() {
653 let mut pos = queue.line_buffer.len();
654 while pos > 0 {
655 pos -= 1;
656 if !is_utf8_continuation(queue.line_buffer[pos], &self.termios) {
657 break;
658 }
659 }
660 let first_byte = queue.line_buffer[pos];
661 if is_utf8_continuation(first_byte, &self.termios) {
662 break;
664 }
665 if erase_type == EraseType::Word {
666 if is_linux_alnum_or_underscore(first_byte) {
667 seen_alnums += 1;
668 } else if seen_alnums > 0 {
669 break;
670 }
671 }
672 let erased_char: Vec<RawByte> = queue.line_buffer.drain(pos..).collect();
673 if self.termios.has_local_flags(ECHO) {
674 if self.termios.has_local_flags(ECHOPRT) {
675 if !self.erasing {
676 self.echo_raw_byte(b'\\');
677 self.erasing = true;
678 }
679 for &b in &erased_char {
680 self.echo_char(b);
681 }
682 } else if erase_type == EraseType::Character && !self.termios.has_local_flags(ECHOE)
683 {
684 self.echo_char(self.termios.c_cc[VERASE as usize]);
685 } else if first_byte == b'\t' {
686 let mut num_chars = 0;
687 let mut after_tab = false;
688 for &b in queue.line_buffer.iter().rev() {
689 if b == b'\t' {
690 after_tab = true;
691 break;
692 } else if is_cntrl(b) {
693 if self.termios.has_local_flags(ECHOCTL) {
694 num_chars += 2;
695 }
696 } else if !is_utf8_continuation(b, &self.termios) {
697 num_chars += 1;
698 }
699 }
700 self.pending_echoes.push(EchoOp::EraseTab { num_chars, after_tab });
701 } else {
702 if is_cntrl(first_byte) && self.termios.has_local_flags(ECHOCTL) {
703 self.echo_raw_byte(BACKSPACE_CHAR);
704 self.echo_raw_byte(b' ');
705 self.echo_raw_byte(BACKSPACE_CHAR);
706 }
707 if !is_cntrl(first_byte) || self.termios.has_local_flags(ECHOCTL) {
708 self.echo_raw_byte(BACKSPACE_CHAR);
709 self.echo_raw_byte(b' ');
710 self.echo_raw_byte(BACKSPACE_CHAR);
711 }
712 }
713 }
714 if erase_type == EraseType::Character {
715 break;
716 }
717 }
718 if queue.line_buffer.is_empty() && self.termios.has_local_flags(ECHO) {
719 self.finish_erasing();
720 }
721 }
722
723 fn transform(
724 &mut self,
725 is_input: bool,
726 queue: &mut Queue,
727 buffer: &[RawByte],
728 ) -> (usize, PendingSignals) {
729 if is_input {
730 self.transform_input(queue, buffer)
731 } else {
732 (self.transform_output(queue, buffer), PendingSignals::new())
733 }
734 }
735
736 fn transform_output(&mut self, queue: &mut Queue, original_buffer: &[RawByte]) -> usize {
737 if self.stopped && self.termios.has_input_flags(IXON) {
738 return 0;
739 }
740 let mut buffer = original_buffer;
741
742 if !self.termios.has_output_flags(OPOST) {
746 let limit = CANON_MAX_BYTES.saturating_sub(queue.readable_size());
747 if limit == 0 {
748 return 0;
749 }
750 let to_write = std::cmp::min(limit, buffer.len());
751 queue
752 .read_queue
753 .push_back(ReadPacket { data: buffer[..to_write].to_vec(), has_eof: false });
754 return to_write;
755 }
756
757 let mut return_value = 0;
758 while !buffer.is_empty()
759 && queue.readable_size() + queue.line_buffer.len() < CANON_MAX_BYTES
760 {
761 let c = buffer[0];
762 return_value += 1;
763 buffer = &buffer[1..];
764 self.do_output_char(c, &mut queue.line_buffer);
765 }
766 if !queue.line_buffer.is_empty() {
767 queue.flush_line_buffer();
768 }
769 return_value
770 }
771
772 fn transform_input(
773 &mut self,
774 queue: &mut Queue,
775 original_buffer: &[RawByte],
776 ) -> (usize, PendingSignals) {
777 let mut buffer = original_buffer;
778
779 let mut return_value = 0;
780 let mut signals = PendingSignals::new();
781 while !buffer.is_empty()
782 && queue.buffer_len()
783 < if self.is_canon_enabled() && queue.read_queue.is_empty() {
784 CANON_MAX_BYTES
785 } else {
786 NON_CANON_MAX_BYTES
787 }
788 {
789 let mut c = buffer[0];
790
791 if self.termios.has_input_flags(ISTRIP) {
792 c &= 0x7f;
793 }
794 if self.termios.has_input_flags(IUCLC) && self.termios.has_local_flags(IEXTEN) {
795 c.make_ascii_lowercase();
796 }
797
798 if self.lnext {
800 self.lnext = false;
801 if self.stopped
802 && self.termios.has_input_flags(IXON)
803 && self.termios.has_input_flags(IXANY)
804 {
805 self.start_tty();
806 }
807 let parmrk_double = c == 0xff && self.termios.has_input_flags(PARMRK);
808 let pushed_len = if parmrk_double { 2 } else { 1 };
809 if queue.buffer_len() + pushed_len > NON_CANON_MAX_BYTES {
810 if self.is_canon_enabled() && queue.read_queue.is_empty() {
811 buffer = &buffer[1..];
812 return_value += 1;
813 continue;
814 }
815 break;
816 }
817 if self.termios.has_local_flags(ECHO) {
818 self.finish_erasing();
819 if queue.line_buffer.is_empty() {
820 self.echo_set_canon_col();
821 }
822 self.echo_char(c);
823 }
824 if parmrk_double {
828 queue.line_buffer.extend_from_slice(&[0xff, 0xff]);
829 } else {
830 queue.line_buffer.push(c);
831 }
832 buffer = &buffer[1..];
833 return_value += 1;
834 continue;
835 }
836
837 if self.is_extproc_enabled() {
839 if queue.buffer_len() + 1 > NON_CANON_MAX_BYTES {
840 break;
841 }
842 queue.line_buffer.push(c);
843 buffer = &buffer[1..];
844 return_value += 1;
845 continue;
846 }
847
848 if self.termios.has_input_flags(IXON) {
850 if c == self.termios.c_cc[VSTART as usize]
851 && self.termios.c_cc[VSTART as usize] != DISABLED_CHAR
852 {
853 self.start_tty();
854 buffer = &buffer[1..];
855 return_value += 1;
856 continue;
857 }
858 if c == self.termios.c_cc[VSTOP as usize]
859 && self.termios.c_cc[VSTOP as usize] != DISABLED_CHAR
860 {
861 self.stop_tty();
862 buffer = &buffer[1..];
863 return_value += 1;
864 continue;
865 }
866 }
867
868 if let Some(signal) = self.handle_signals(c) {
870 signals.add(signal);
871 if !self.termios.has_local_flags(NOFLSH) {
872 queue.flush_buffers();
873 self.reset_input_state();
874 if let Some(ref mut output_queue) = self.output_queue {
875 output_queue.flush();
876 }
877 if self.packet_mode_enabled {
878 self.packet_mode_pending_events |=
879 (uapi::TIOCPKT_FLUSHREAD | uapi::TIOCPKT_FLUSHWRITE) as u8;
880 }
881 }
882 if self.termios.has_input_flags(IXON) {
883 self.start_tty();
884 }
885 if self.termios.has_local_flags(ECHO) {
886 self.echo_char(c);
887 self.commit_echoes();
888 }
889 buffer = &buffer[1..];
890 return_value += 1;
891 continue;
892 }
893
894 if self.stopped
896 && self.termios.has_input_flags(IXON)
897 && self.termios.has_input_flags(IXANY)
898 {
899 self.start_tty();
900 }
901
902 match c {
904 b'\r' => {
905 if self.termios.has_input_flags(IGNCR) {
906 buffer = &buffer[1..];
907 return_value += 1;
908 continue;
909 }
910 if self.termios.has_input_flags(ICRNL) {
911 c = b'\n';
912 }
913 }
914 b'\n' => {
915 if self.termios.has_input_flags(INLCR) {
916 c = b'\r';
917 }
918 }
919 _ => {}
920 }
921
922 if self.is_canon_enabled() {
924 if self.termios.is_erase(c) || self.termios.is_kill(c) || self.termios.is_werase(c)
925 {
926 self.eraser(queue, c);
927 buffer = &buffer[1..];
928 return_value += 1;
929 continue;
930 }
931
932 if self.termios.has_local_flags(IEXTEN) {
933 if c == self.termios.c_cc[VLNEXT as usize]
934 && self.termios.c_cc[VLNEXT as usize] != DISABLED_CHAR
935 {
936 self.lnext = true;
937 if self.termios.has_local_flags(ECHO) {
938 self.finish_erasing();
939 if self.termios.has_local_flags(ECHOCTL) {
940 self.echo_raw_byte(b'^');
941 self.echo_raw_byte(BACKSPACE_CHAR);
942 }
943 }
944 buffer = &buffer[1..];
945 return_value += 1;
946 continue;
947 }
948
949 if c == self.termios.c_cc[VREPRINT as usize]
950 && self.termios.c_cc[VREPRINT as usize] != DISABLED_CHAR
951 && self.termios.has_local_flags(ECHO)
952 {
953 self.finish_erasing();
954 self.echo_char(c);
955 self.echo_raw_byte(b'\n');
956 for &b in &queue.line_buffer {
957 self.echo_char(b);
958 }
959 buffer = &buffer[1..];
960 return_value += 1;
961 continue;
962 }
963 }
964
965 if c == b'\n' {
966 if self.termios.has_local_flags(ECHO) || self.termios.has_local_flags(ECHONL) {
967 self.echo_raw_byte(b'\n');
968 }
969 queue.line_buffer.push(b'\n');
970 queue.flush_line_buffer();
971 buffer = &buffer[1..];
972 return_value += 1;
973 continue;
974 }
975
976 if self.termios.is_eof(c) {
977 let data = std::mem::take(&mut queue.line_buffer);
978 queue.read_queue.push_back(ReadPacket { data, has_eof: true });
979 buffer = &buffer[1..];
980 return_value += 1;
981 continue;
982 }
983
984 if self.termios.is_eol(c) {
985 let parmrk_double = c == 0xff && self.termios.has_input_flags(PARMRK);
986 let pushed_len = if parmrk_double { 2 } else { 1 };
987 let max_bytes = if queue.read_queue.is_empty() {
988 CANON_MAX_BYTES
989 } else {
990 NON_CANON_MAX_BYTES
991 };
992 if queue.buffer_len() + pushed_len > max_bytes {
993 if queue.read_queue.is_empty() {
994 buffer = &buffer[1..];
995 return_value += 1;
996 continue;
997 }
998 break;
999 }
1000 if self.termios.has_local_flags(ECHO) {
1004 if queue.line_buffer.is_empty() {
1005 self.echo_set_canon_col();
1006 }
1007 self.echo_char(c);
1008 }
1009 if parmrk_double {
1010 queue.line_buffer.extend_from_slice(&[0xff, 0xff]);
1011 } else {
1012 queue.line_buffer.push(c);
1013 }
1014 queue.flush_line_buffer();
1015 buffer = &buffer[1..];
1016 return_value += 1;
1017 continue;
1018 }
1019 }
1020
1021 let parmrk_double = c == 0xff && self.termios.has_input_flags(PARMRK);
1023 let pushed_len = if parmrk_double { 2 } else { 1 };
1024 if queue.buffer_len() + pushed_len > NON_CANON_MAX_BYTES {
1025 if self.is_canon_enabled() && queue.read_queue.is_empty() {
1026 buffer = &buffer[1..];
1027 return_value += 1;
1028 continue;
1029 }
1030 break;
1031 }
1032
1033 if self.termios.has_local_flags(ECHO) {
1034 self.finish_erasing();
1035 if queue.line_buffer.is_empty() {
1036 self.echo_set_canon_col();
1037 }
1038 self.echo_char(c);
1039 }
1040
1041 if parmrk_double {
1042 queue.line_buffer.extend_from_slice(&[0xff, 0xff]);
1043 } else {
1044 queue.line_buffer.push(c);
1045 }
1046 buffer = &buffer[1..];
1047 return_value += 1;
1048 }
1049
1050 self.commit_echoes();
1051
1052 if !self.is_canon_enabled() && !queue.line_buffer.is_empty() {
1054 queue.flush_line_buffer();
1055 }
1056
1057 (return_value, signals)
1058 }
1059}
1060
1061type RawByte = u8;
1064
1065#[derive(Debug, Default)]
1066struct ReadPacket {
1067 data: Vec<u8>,
1068 has_eof: bool,
1069}
1070
1071#[derive(Debug, Default)]
1072struct Queue {
1073 read_queue: VecDeque<ReadPacket>,
1076
1077 line_buffer: Vec<u8>,
1081
1082 wait_buffers: VecDeque<Vec<RawByte>>,
1085
1086 total_wait_buffer_length: usize,
1088
1089 is_input: bool,
1091}
1092
1093impl Queue {
1094 fn output_queue() -> Option<Self> {
1095 Some(Queue { is_input: false, ..Default::default() })
1096 }
1097
1098 fn input_queue() -> Option<Self> {
1099 Some(Queue { is_input: true, ..Default::default() })
1100 }
1101
1102 fn write_readiness(&self) -> FdEvents {
1104 if self.total_wait_buffer_length < WAIT_BUFFER_MAX_BYTES {
1105 FdEvents::POLLOUT
1106 } else {
1107 FdEvents::empty()
1108 }
1109 }
1110
1111 fn read_readiness(&self) -> FdEvents {
1113 if !self.read_queue.is_empty() { FdEvents::POLLIN } else { FdEvents::empty() }
1115 }
1116
1117 fn readable_size(&self) -> usize {
1119 self.read_queue.iter().map(|p| p.data.len()).sum()
1122 }
1123
1124 fn buffer_len(&self) -> usize {
1127 self.read_queue.iter().map(|p| p.data.len() + usize::from(p.has_eof)).sum::<usize>()
1128 + self.line_buffer.len()
1129 }
1130
1131 fn read(
1134 &mut self,
1135 terminal: &mut LineDiscipline,
1136 data: &mut dyn OutputBuffer,
1137 ) -> Result<(usize, PendingSignals), Errno> {
1138 if self.read_queue.is_empty() {
1139 return error!(EAGAIN);
1140 }
1141 if data.available() == 0 {
1142 return Ok((0, PendingSignals::new()));
1143 }
1144
1145 let mut total_written = 0;
1146 while let Some(mut packet) = self.read_queue.pop_front() {
1147 if packet.data.is_empty() {
1148 if total_written > 0 {
1149 self.read_queue.push_front(packet);
1152 }
1153 break;
1154 }
1155
1156 match data.write(&packet.data) {
1157 Ok(written) => {
1158 total_written += written;
1159 if written < packet.data.len() {
1160 let remaining = packet.data.split_off(written);
1162 self.read_queue
1163 .push_front(ReadPacket { data: remaining, has_eof: packet.has_eof });
1164 break;
1166 }
1167
1168 if (self.is_input && terminal.is_canon_enabled()) || packet.has_eof {
1171 break;
1172 }
1173 }
1174 Err(e) => {
1175 self.read_queue.push_front(packet);
1177 if total_written > 0 {
1178 let signals = self.drain_waiting_buffer(terminal);
1180 return Ok((total_written, signals));
1181 }
1182 return Err(e);
1183 }
1184 }
1185 }
1186
1187 let signals = self.drain_waiting_buffer(terminal);
1188 Ok((total_written, signals))
1189 }
1190
1191 fn write(
1193 &mut self,
1194 terminal: &mut LineDiscipline,
1195 data: &mut dyn InputBuffer,
1196 ) -> Result<(usize, PendingSignals), Errno> {
1197 let room = WAIT_BUFFER_MAX_BYTES - self.total_wait_buffer_length;
1198 let data_length = data.available();
1199 if room == 0 && data_length > 0 {
1200 return error!(EAGAIN);
1201 }
1202 let buffer = data.read_to_vec_exact(std::cmp::min(room, data_length))?;
1203 let read_from_userspace = buffer.len();
1204 let signals = self.push_to_waiting_buffer(terminal, buffer);
1205 Ok((read_from_userspace, signals))
1206 }
1207
1208 fn push_to_waiting_buffer(
1210 &mut self,
1211 terminal: &mut LineDiscipline,
1212 buffer: Vec<RawByte>,
1213 ) -> PendingSignals {
1214 self.total_wait_buffer_length += buffer.len();
1215 self.wait_buffers.push_back(buffer);
1216 self.drain_waiting_buffer(terminal)
1217 }
1218
1219 fn drain_waiting_buffer(&mut self, terminal: &mut LineDiscipline) -> PendingSignals {
1221 let mut signals_to_return = PendingSignals::new();
1222 while let Some(wait_buffer) = self.wait_buffers.pop_front() {
1223 self.total_wait_buffer_length -= wait_buffer.len();
1224 let (count, signals) = terminal.transform(self.is_input, self, &wait_buffer);
1225 signals_to_return.append(signals);
1226 if count != wait_buffer.len() {
1227 let remaining = wait_buffer[count..].to_vec();
1228 self.total_wait_buffer_length += remaining.len();
1229 self.wait_buffers.push_front(remaining);
1230 break;
1231 }
1232 }
1233 signals_to_return
1234 }
1235
1236 fn flush_line_buffer(&mut self) {
1238 self.read_queue
1239 .push_back(ReadPacket { data: std::mem::take(&mut self.line_buffer), has_eof: false });
1240 }
1241
1242 fn flush(&mut self) {
1244 self.flush_buffers();
1245 self.flush_unprocessed();
1246 }
1247
1248 fn flush_buffers(&mut self) {
1250 self.read_queue.clear();
1251 self.line_buffer.clear();
1252 }
1253
1254 fn flush_unprocessed(&mut self) {
1256 self.wait_buffers.clear();
1257 self.total_wait_buffer_length = 0;
1258 }
1259
1260 fn on_canon_mode_changed(&mut self, terminal: &mut LineDiscipline) -> PendingSignals {
1262 let mut combined = Vec::new();
1263 for packet in self.read_queue.drain(..) {
1264 combined.extend(packet.data);
1265 if packet.has_eof {
1266 combined.push(DISABLED_CHAR);
1267 }
1268 }
1269 combined.append(&mut self.line_buffer);
1270 if !combined.is_empty() {
1271 let has_eof = terminal.is_canon_enabled() && combined.last() == Some(&DISABLED_CHAR);
1272 if has_eof {
1273 combined.pop();
1274 }
1275 self.read_queue.push_back(ReadPacket { data: combined, has_eof });
1276 }
1277 self.drain_waiting_buffer(terminal)
1278 }
1279}
1280
1281fn get_ascii(c: char) -> u8 {
1285 let mut dest: [u8; 1] = [0];
1286 c.encode_utf8(&mut dest);
1287 dest[0]
1288}
1289
1290fn get_control_character(c: char) -> cc_t {
1292 get_ascii(c) - get_ascii('A') + 1
1293}
1294
1295fn get_default_control_characters() -> [cc_t; 19usize] {
1297 [
1298 get_control_character('C'), get_control_character('\\'), get_ascii('\x7f'), get_control_character('U'), get_control_character('D'), 0, 1, 0, get_control_character('Q'), get_control_character('S'), get_control_character('Z'), 0, get_control_character('R'), get_control_character('O'), get_control_character('W'), get_control_character('V'), 0, 0, 0, ]
1318}
1319
1320const DEFAULT_SPEED: u32 = 38400;
1321
1322pub fn get_default_termios() -> uapi::termios2 {
1324 uapi::termios2 {
1325 c_iflag: uapi::ICRNL | uapi::IXON,
1326 c_oflag: uapi::OPOST | uapi::ONLCR,
1327 c_cflag: uapi::B38400 | uapi::CS8 | uapi::CREAD,
1328 c_lflag: uapi::ISIG
1329 | uapi::ICANON
1330 | uapi::ECHO
1331 | uapi::ECHOE
1332 | uapi::ECHOK
1333 | uapi::ECHOCTL
1334 | uapi::ECHOKE
1335 | uapi::IEXTEN,
1336 c_line: 0,
1337 c_cc: get_default_control_characters(),
1338 c_ispeed: DEFAULT_SPEED,
1339 c_ospeed: DEFAULT_SPEED,
1340 }
1341}
1342
1343trait TermIOS {
1345 fn has_input_flags(&self, flags: tcflag_t) -> bool;
1346 fn has_output_flags(&self, flags: tcflag_t) -> bool;
1347 fn has_local_flags(&self, flags: tcflag_t) -> bool;
1348 fn is_eof(&self, c: RawByte) -> bool;
1349 fn is_erase(&self, c: RawByte) -> bool;
1350 fn is_werase(&self, c: RawByte) -> bool;
1351 fn is_kill(&self, c: RawByte) -> bool;
1352 fn is_eol(&self, c: RawByte) -> bool;
1353 fn signal(&self, c: RawByte) -> Option<Signal>;
1354}
1355
1356impl TermIOS for uapi::termios2 {
1357 fn has_input_flags(&self, flags: tcflag_t) -> bool {
1358 self.c_iflag & flags == flags
1359 }
1360 fn has_output_flags(&self, flags: tcflag_t) -> bool {
1361 self.c_oflag & flags == flags
1362 }
1363 fn has_local_flags(&self, flags: tcflag_t) -> bool {
1364 self.c_lflag & flags == flags
1365 }
1366 fn is_eof(&self, c: RawByte) -> bool {
1367 c == self.c_cc[VEOF as usize] && self.c_cc[VEOF as usize] != DISABLED_CHAR
1368 }
1369 fn is_erase(&self, c: RawByte) -> bool {
1370 c == self.c_cc[VERASE as usize] && self.c_cc[VERASE as usize] != DISABLED_CHAR
1371 }
1372 fn is_werase(&self, c: RawByte) -> bool {
1373 c == self.c_cc[VWERASE as usize]
1374 && self.c_cc[VWERASE as usize] != DISABLED_CHAR
1375 && self.has_local_flags(IEXTEN)
1376 }
1377 fn is_kill(&self, c: RawByte) -> bool {
1378 c == self.c_cc[VKILL as usize] && self.c_cc[VKILL as usize] != DISABLED_CHAR
1379 }
1380 fn is_eol(&self, c: RawByte) -> bool {
1381 if c == DISABLED_CHAR {
1382 return false;
1383 }
1384 if c == self.c_cc[VEOL as usize] {
1385 return true;
1386 }
1387 if c == self.c_cc[VEOL2 as usize] {
1388 return self.has_local_flags(IEXTEN);
1389 }
1390 false
1391 }
1392 fn signal(&self, c: RawByte) -> Option<Signal> {
1393 if c == DISABLED_CHAR {
1394 return None;
1395 }
1396 if c == self.c_cc[VINTR as usize] {
1397 return Some(SIGINT);
1398 }
1399 if c == self.c_cc[VQUIT as usize] {
1400 return Some(SIGQUIT);
1401 }
1402 if c == self.c_cc[VSUSP as usize] {
1403 return Some(SIGTSTP);
1404 }
1405 None
1406 }
1407}
1408
1409fn is_cntrl(c: RawByte) -> bool {
1410 c <= 0x1f || c == 0x7f
1411}
1412
1413fn is_linux_alnum_or_underscore(c: RawByte) -> bool {
1414 c.is_ascii_alphanumeric() || c == b'_' || matches!(c, 0xc0..=0xd6 | 0xd8..=0xf6 | 0xf8..=0xff)
1415}
1416
1417fn is_utf8_continuation(c: RawByte, termios: &uapi::termios2) -> bool {
1418 termios.has_input_flags(IUTF8) && (c & 0xc0) == 0x80
1419}
1420
1421#[cfg(test)]
1422mod tests {
1423 use super::*;
1424
1425 #[::fuchsia::test]
1426 fn test_ascii_conversion() {
1427 assert_eq!(get_ascii(' '), 32);
1428 }
1429
1430 #[::fuchsia::test]
1431 fn test_control_character() {
1432 assert_eq!(get_control_character('C'), 3);
1433 }
1434
1435 #[::fuchsia::test]
1436 fn test_signal_handling_with_disabled_chars() {
1437 let mut termios = get_default_termios();
1438 assert_eq!(termios.signal(3), Some(SIGINT));
1439 assert_eq!(termios.signal(28), Some(SIGQUIT));
1440 assert_eq!(termios.signal(26), Some(SIGTSTP));
1441
1442 termios.c_cc[VINTR as usize] = DISABLED_CHAR;
1443 termios.c_cc[VQUIT as usize] = DISABLED_CHAR;
1444 termios.c_cc[VSUSP as usize] = DISABLED_CHAR;
1445
1446 assert_eq!(termios.signal(0), None);
1447 assert_eq!(termios.signal(3), None); assert_eq!(termios.signal(28), None); assert_eq!(termios.signal(26), None); }
1451
1452 struct TestBuffer {
1453 data: Vec<u8>,
1454 }
1455
1456 impl InputBuffer for TestBuffer {
1457 fn available(&self) -> usize {
1458 self.data.len()
1459 }
1460 fn read_to_vec_exact(&mut self, size: usize) -> Result<Vec<u8>, Errno> {
1461 if size > self.data.len() {
1462 return error!(EAGAIN);
1463 }
1464 Ok(self.data.drain(0..size).collect())
1465 }
1466 }
1467
1468 impl OutputBuffer for TestBuffer {
1469 fn available(&self) -> usize {
1470 usize::MAX
1471 }
1472 fn write(&mut self, data: &[u8]) -> Result<usize, Errno> {
1473 self.data.extend_from_slice(data);
1474 Ok(data.len())
1475 }
1476 }
1477
1478 #[::fuchsia::test]
1479 fn test_flush() {
1480 fn make_ld() -> LineDiscipline {
1481 let mut ld = LineDiscipline::default();
1482 ld.main_open();
1483 ld.replica_open();
1484
1485 let mut termios = get_default_termios();
1486 termios.c_lflag &= !ECHO;
1487 termios.c_oflag &= !OPOST;
1488 let _ = ld.set_termios(termios);
1489
1490 let mut input = TestBuffer { data: b"ping\n".to_vec() };
1493 let (written, signals) = ld.main_write(&mut input).unwrap();
1494 assert_eq!(written, 5);
1495 assert!(signals.signals().is_empty());
1496
1497 let mut output = TestBuffer { data: b"pong\n".to_vec() };
1500 let written = ld.replica_write(&mut output).unwrap();
1501 assert_eq!(written, 5);
1502 ld
1503 }
1504
1505 let mut read_buf = TestBuffer { data: vec![] };
1506
1507 let mut ld = make_ld();
1509 ld.flush(TerminalSide::Main, uapi::TCIFLUSH).unwrap();
1510 read_buf.data.clear();
1511 assert_eq!(error!(EAGAIN), ld.main_read(&mut read_buf));
1512 assert!(read_buf.data.is_empty());
1513 read_buf.data.clear();
1514 assert!(ld.replica_read(&mut read_buf).is_ok());
1515 assert_eq!(read_buf.data, b"ping\n");
1516
1517 let mut ld = make_ld();
1519 ld.flush(TerminalSide::Replica, uapi::TCIFLUSH).unwrap();
1520 read_buf.data.clear();
1521 assert!(ld.main_read(&mut read_buf).is_ok());
1522 assert_eq!(read_buf.data, b"pong\n");
1523 read_buf.data.clear();
1524 assert_eq!(error!(EAGAIN), ld.replica_read(&mut read_buf));
1525 assert!(read_buf.data.is_empty());
1526
1527 let mut ld = make_ld();
1529 ld.flush(TerminalSide::Main, uapi::TCOFLUSH).unwrap();
1530 read_buf.data.clear();
1531 assert!(ld.main_read(&mut read_buf).is_ok());
1532 assert_eq!(read_buf.data, b"pong\n");
1533 read_buf.data.clear();
1534 assert!(ld.replica_read(&mut read_buf).is_ok());
1535 assert_eq!(read_buf.data, b"ping\n");
1536
1537 let mut ld = make_ld();
1539 ld.flush(TerminalSide::Replica, uapi::TCOFLUSH).unwrap();
1540 read_buf.data.clear();
1541 assert!(ld.main_read(&mut read_buf).is_ok());
1542 assert_eq!(read_buf.data, b"pong\n");
1543 read_buf.data.clear();
1544 assert!(ld.replica_read(&mut read_buf).is_ok());
1545 assert_eq!(read_buf.data, b"ping\n");
1546
1547 let mut ld = make_ld();
1549 ld.flush(TerminalSide::Main, uapi::TCIOFLUSH).unwrap();
1550 read_buf.data.clear();
1551 assert_eq!(error!(EAGAIN), ld.main_read(&mut read_buf));
1552 read_buf.data.clear();
1553 assert!(ld.replica_read(&mut read_buf).is_ok());
1554 assert_eq!(read_buf.data, b"ping\n");
1555
1556 let mut ld = make_ld();
1558 ld.flush(TerminalSide::Replica, uapi::TCIOFLUSH).unwrap();
1559 read_buf.data.clear();
1560 assert!(ld.main_read(&mut read_buf).is_ok());
1561 assert_eq!(read_buf.data, b"pong\n");
1562 read_buf.data.clear();
1563 assert_eq!(error!(EAGAIN), ld.replica_read(&mut read_buf));
1564
1565 let mut ld = LineDiscipline::default();
1567 ld.main_open();
1568 ld.replica_open();
1569 let mut stop_and_text = TestBuffer { data: b"\x13stale\n".to_vec() };
1570 let _ = ld.main_write(&mut stop_and_text).unwrap();
1571 ld.flush(TerminalSide::Replica, uapi::TCIFLUSH).unwrap();
1572 let mut start_in = TestBuffer { data: b"\x11".to_vec() };
1573 let _ = ld.main_write(&mut start_in).unwrap();
1574 read_buf.data.clear();
1575 assert_eq!(error!(EAGAIN), ld.main_read(&mut read_buf));
1576 }
1577
1578 #[::fuchsia::test]
1579 fn test_canonical_max_line_length_with_erase_and_eof() {
1580 let mut ld = LineDiscipline::default();
1581 ld.main_open();
1582 ld.replica_open();
1583
1584 let mut payload = vec![b'A'; 4096];
1586 payload.extend_from_slice(b"\x7fB\n");
1587 let mut input = TestBuffer { data: payload };
1588 let (written, signals) = ld.main_write(&mut input).unwrap();
1589 assert_eq!(written, 4099);
1590 assert!(signals.signals().is_empty());
1591
1592 let mut replica_out = TestBuffer { data: vec![] };
1593 let (n, signals) = ld.replica_read(&mut replica_out).unwrap();
1594 assert_eq!(n, 4096);
1595 assert!(signals.signals().is_empty());
1596 assert_eq!(&replica_out.data[..4094], &[b'A'; 4094][..]);
1597 assert_eq!(&replica_out.data[4094..], b"B\n");
1598
1599 let mut main_out = TestBuffer { data: vec![] };
1600 let _ = ld.main_read(&mut main_out).unwrap();
1601 assert_eq!(&main_out.data[..4095], &[b'A'; 4095][..]);
1602 assert_eq!(&main_out.data[4095..], b"\x08 \x08B\r\n");
1603
1604 let mut payload_eof = vec![b'C'; 4096];
1606 payload_eof.push(4);
1607 let mut input_eof = TestBuffer { data: payload_eof };
1608 let (written, _) = ld.main_write(&mut input_eof).unwrap();
1609 assert_eq!(written, 4097);
1610
1611 replica_out.data.clear();
1612 let (n, signals) = ld.replica_read(&mut replica_out).unwrap();
1613 assert_eq!(n, 4095);
1614 assert!(signals.signals().is_empty());
1615 assert_eq!(replica_out.data, vec![b'C'; 4095]);
1616 replica_out.data.clear();
1617 assert_eq!(ld.replica_read(&mut replica_out), error!(EAGAIN));
1618 }
1619
1620 #[::fuchsia::test]
1621 fn test_pty_cflag_enforced() {
1622 let mut ld = LineDiscipline::default();
1623 let mut termios = get_default_termios();
1624 termios.c_cflag = uapi::CS5 | uapi::PARENB;
1625 let _ = ld.set_termios(termios);
1626 assert_eq!(ld.termios().c_cflag & CSIZE, CS8);
1627 assert_eq!(ld.termios().c_cflag & CREAD, CREAD);
1628 assert_eq!(ld.termios().c_cflag & PARENB, 0);
1629 }
1630
1631 #[::fuchsia::test]
1632 fn test_close_events_and_eio() {
1633 let mut ld = LineDiscipline::default();
1634 ld.main_open();
1635 ld.replica_open();
1636
1637 ld.replica_close();
1638 assert!(ld.is_replica_closed());
1639 assert_eq!(ld.main_query_events(), FdEvents::POLLOUT | FdEvents::POLLHUP);
1640 let mut read_buf = TestBuffer { data: vec![] };
1641 assert_eq!(ld.main_read(&mut read_buf), error!(EIO));
1642
1643 ld.replica_open();
1644 let mut main_in = TestBuffer { data: b"unread\n".to_vec() };
1645 let _ = ld.main_write(&mut main_in).unwrap();
1646 assert_eq!(ld.get_available_read_size(TerminalSide::Replica), 7);
1647
1648 ld.main_close();
1649 assert!(ld.is_main_closed());
1650 assert_eq!(ld.get_available_read_size(TerminalSide::Replica), 0);
1651 assert_eq!(ld.get_available_read_size(TerminalSide::Main), 0);
1652 assert_eq!(
1653 ld.replica_query_events(),
1654 FdEvents::POLLIN | FdEvents::POLLOUT | FdEvents::POLLERR | FdEvents::POLLHUP
1655 );
1656 let (n, signals) = ld.replica_read(&mut read_buf).unwrap();
1657 assert_eq!(n, 0);
1658 assert!(signals.signals().is_empty());
1659 let mut write_buf = TestBuffer { data: b"x".to_vec() };
1660 assert_eq!(ld.replica_write(&mut write_buf), error!(EIO));
1661 }
1662
1663 #[::fuchsia::test]
1664 fn test_parmrk_capacity_limits() {
1665 let mut ld = LineDiscipline::default();
1666 ld.main_open();
1667 ld.replica_open();
1668
1669 let mut termios = get_default_termios();
1670 termios.c_iflag |= PARMRK;
1671 termios.c_lflag &= !ECHO;
1672 termios.c_cc[VEOL as usize] = b';';
1673 let _ = ld.set_termios(termios);
1674
1675 let mut payload = vec![b'A'; 4094];
1678 payload.extend_from_slice(&[0xff, b'\n']);
1679 let mut input = TestBuffer { data: payload };
1680 let (written, _) = ld.main_write(&mut input).unwrap();
1681 assert_eq!(written, 4096);
1682
1683 let mut replica_out = TestBuffer { data: vec![] };
1684 let (n, _) = ld.replica_read(&mut replica_out).unwrap();
1685 assert_eq!(n, 4095);
1686 assert_eq!(&replica_out.data[..4094], &[b'A'; 4094][..]);
1687 assert_eq!(replica_out.data[4094], b'\n');
1688
1689 termios.c_cc[VEOL as usize] = 0xff;
1692 let _ = ld.set_termios(termios);
1693 let mut payload_veol = vec![b'A'; 4095];
1694 payload_veol.extend_from_slice(&[0xff, b'\n']);
1695 let mut input_veol = TestBuffer { data: payload_veol };
1696 let _ = ld.main_write(&mut input_veol).unwrap();
1697 replica_out.data.clear();
1698 let (n, _) = ld.replica_read(&mut replica_out).unwrap();
1699 assert_eq!(n, 4096);
1700 assert_eq!(&replica_out.data[..4095], &[b'A'; 4095][..]);
1701 assert_eq!(replica_out.data[4095], b'\n');
1702
1703 termios.c_lflag &= !ICANON;
1706 let _ = ld.set_termios(termios);
1707 let mut payload_noncanon = vec![b'A'; 4094];
1708 payload_noncanon.push(0xff);
1709 let mut input_noncanon = TestBuffer { data: payload_noncanon };
1710 let (written, _) = ld.main_write(&mut input_noncanon).unwrap();
1711 assert_eq!(written, 4095);
1712 assert_eq!(ld.get_available_read_size(TerminalSide::Replica), 4094);
1713 replica_out.data.clear();
1714 let (n, _) = ld.replica_read(&mut replica_out).unwrap();
1715 assert_eq!(n, 4094);
1716 replica_out.data.clear();
1717 let (n, _) = ld.replica_read(&mut replica_out).unwrap();
1718 assert_eq!(n, 2);
1719 assert_eq!(replica_out.data, vec![0xff, 0xff]);
1720 }
1721
1722 #[::fuchsia::test]
1723 fn test_stopped_output_wait_buffer_not_drained_until_start() {
1724 let mut ld = LineDiscipline::default();
1725 ld.main_open();
1726 ld.replica_open();
1727
1728 let mut replica_in = TestBuffer { data: vec![b'A'; 5000] };
1731 let written = ld.replica_write(&mut replica_in).unwrap();
1732 assert_eq!(written, 5000);
1733 assert_eq!(ld.get_available_read_size(TerminalSide::Main), 4096);
1734
1735 let mut stop_in = TestBuffer { data: vec![0x13] };
1737 let (written, signals) = ld.main_write(&mut stop_in).unwrap();
1738 assert_eq!(written, 1);
1739 assert!(signals.signals().is_empty());
1740
1741 let mut main_out = TestBuffer { data: vec![] };
1744 let n = ld.main_read(&mut main_out).unwrap();
1745 assert_eq!(n, 4096);
1746 assert_eq!(main_out.data.len(), 4096);
1747 assert_eq!(ld.get_available_read_size(TerminalSide::Main), 0);
1748
1749 main_out.data.clear();
1750 assert_eq!(ld.main_read(&mut main_out), error!(EAGAIN));
1751
1752 let mut start_in = TestBuffer { data: vec![0x11] };
1754 let (written, signals) = ld.main_write(&mut start_in).unwrap();
1755 assert_eq!(written, 1);
1756 assert!(signals.signals().is_empty());
1757 assert_eq!(ld.get_available_read_size(TerminalSide::Main), 904);
1758
1759 let n = ld.main_read(&mut main_out).unwrap();
1760 assert_eq!(n, 904);
1761 assert_eq!(main_out.data, vec![b'A'; 904]);
1762 }
1763
1764 #[::fuchsia::test]
1765 fn test_veof_and_unread_queue_buffer_capacity() {
1766 let mut ld = LineDiscipline::default();
1767 ld.main_open();
1768 ld.replica_open();
1769
1770 let mut eofs = TestBuffer { data: vec![0x04; 4096] };
1774 let (written, _) = ld.main_write(&mut eofs).unwrap();
1775 assert_eq!(written, 4096);
1776 assert_eq!(ld.input_queue().read_queue.len(), NON_CANON_MAX_BYTES);
1777 assert_eq!(ld.input_queue().total_wait_buffer_length, 1);
1778
1779 let mut replica_out = TestBuffer { data: vec![] };
1781 let (n, _) = ld.replica_read(&mut replica_out).unwrap();
1782 assert_eq!(n, 0);
1783 assert_eq!(ld.input_queue().read_queue.len(), NON_CANON_MAX_BYTES);
1784 assert_eq!(ld.input_queue().total_wait_buffer_length, 0);
1785 }
1786
1787 #[::fuchsia::test]
1788 fn test_iutf8_multibyte_at_canon_boundary() {
1789 let mut ld = LineDiscipline::default();
1790 ld.main_open();
1791 ld.replica_open();
1792
1793 let mut termios = get_default_termios();
1794 termios.c_iflag |= IUTF8;
1795 termios.c_lflag &= !ECHO;
1796 let _ = ld.set_termios(termios);
1797
1798 let mut payload = vec![b'A'; 4094];
1801 payload.extend_from_slice(b"\xc3\xa9\n");
1802 let mut input = TestBuffer { data: payload };
1803 let (written, _) = ld.main_write(&mut input).unwrap();
1804 assert_eq!(written, 4097);
1805
1806 let mut replica_out = TestBuffer { data: vec![] };
1807 let (n, _) = ld.replica_read(&mut replica_out).unwrap();
1808 assert_eq!(n, 4096);
1809 assert_eq!(&replica_out.data[..4094], &[b'A'; 4094][..]);
1810 assert_eq!(&replica_out.data[4094..], b"\xc3\n");
1811 }
1812
1813 #[::fuchsia::test]
1814 fn test_signal_flush_preserves_subsequent_wait_buffers() {
1815 let mut ld = LineDiscipline::default();
1816 ld.main_open();
1817 ld.replica_open();
1818
1819 let mut fill = vec![b'A'; 4094];
1822 fill.push(b'\n');
1823 let mut fill_in = TestBuffer { data: fill };
1824 let (written, _) = ld.main_write(&mut fill_in).unwrap();
1825 assert_eq!(written, 4095);
1826
1827 let mut sig_in = TestBuffer { data: b"\x03".to_vec() };
1830 let (written, signals) = ld.main_write(&mut sig_in).unwrap();
1831 assert_eq!(written, 1);
1832 assert!(signals.signals().is_empty());
1833
1834 let mut after_in = TestBuffer { data: b"after\n".to_vec() };
1835 let (written, signals) = ld.main_write(&mut after_in).unwrap();
1836 assert_eq!(written, 6);
1837 assert!(signals.signals().is_empty());
1838
1839 let mut replica_out = TestBuffer { data: vec![] };
1842 let (n, signals) = ld.replica_read(&mut replica_out).unwrap();
1843 assert_eq!(n, 4095);
1844 assert_eq!(signals.signals(), &[SIGINT]);
1845
1846 replica_out.data.clear();
1847 let (n, signals) = ld.replica_read(&mut replica_out).unwrap();
1848 assert_eq!(n, 6);
1849 assert!(signals.signals().is_empty());
1850 assert_eq!(replica_out.data, b"after\n");
1851 }
1852}
1853
1854pub mod testing;