diff --git a/Cargo.lock b/Cargo.lock index 8aeb438..0a8fcbb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,7 +183,7 @@ dependencies = [ ] [[package]] -name = "pty_driver" +name = "pty_server" version = "0.1.0" dependencies = [ "dev_driver_rpc", diff --git a/src/main.rs b/src/main.rs index bd60ba6..079b34c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +mod termios; + use std::{ borrow::Cow, collections::HashMap, @@ -8,10 +10,12 @@ use std::{ use parking_lot::{Mutex, RwLock}; use slab::Slab; +use termios::Termios; struct Pty { input_buffer: Mutex>, output_buffer: Mutex>, + termios: Termios, } impl Pty { @@ -19,6 +23,7 @@ impl Pty { Pty { input_buffer: Mutex::new(Vec::new()), output_buffer: Mutex::new(Vec::new()), + termios: Termios::default(), } } @@ -31,16 +36,41 @@ impl Pty { } fn write_master(&self, data: &[u8]) -> Result<(), ()> { + if !self.termios.cflags.cread { + return Ok(()) + } let mut input_buffer = self.input_buffer.lock(); let mut output_buffer = self.output_buffer.lock(); - for &byte in data { - output_buffer.push(byte); - if byte == 8 { + for &(mut byte) in data { + if self.termios.iflags.istrip { + byte &= 0x7F; + } + if byte == b'\r' && self.termios.iflags.igncr { + continue; + } else if byte == b'\r' && self.termios.iflags.icrnl { + byte = b'\n'; + } else if byte == b'\n' && self.termios.iflags.inlcr { + byte = b'\r'; + } + if byte as char == self.termios.verase && self.termios.lflags.icanon { + if self.termios.lflags.echoe { + output_buffer.push(byte); + } if input_buffer.last() != Some(&b'\n') { input_buffer.pop(); } + } else if byte as char == self.termios.vkill && self.termios.lflags.icanon { + while input_buffer.last().is_some() && input_buffer.last() != Some(&b'\n') { + if self.termios.lflags.echok { + output_buffer.push(8); + } + input_buffer.pop(); + } } else { input_buffer.push(byte); + if self.termios.lflags.echo || (byte == b'\n' && self.termios.lflags.echonl) { + output_buffer.push(byte); + } } } Ok(()) @@ -60,7 +90,19 @@ impl Pty { } fn write_slave(&self, data: &[u8]) -> Result<(), ()> { - self.output_buffer.lock().extend(data); + let mut output_buffer = self.output_buffer.lock(); + for &byte in data { + if self.termios.oflags.opost { + if byte == b'\n' && self.termios.oflags.onlcr { + output_buffer.push(b'\r'); + output_buffer.push(b'\n'); + } else if byte == b'\r' && self.termios.oflags.ocrnl { + output_buffer.push(b'\n'); + } else { + output_buffer.push(byte); + } + } + } Ok(()) } } diff --git a/src/termios.rs b/src/termios.rs new file mode 100644 index 0000000..412cca5 --- /dev/null +++ b/src/termios.rs @@ -0,0 +1,212 @@ +#[allow(unused)] +pub struct Iflags { + pub brkint: bool, + pub icrnl: bool, + pub ignbrk: bool, + pub igncr: bool, + pub ignpar: bool, + pub inlcr: bool, + pub inpck: bool, + pub istrip: bool, + pub ixany: bool, + pub ixoff: bool, + pub ixon: bool, + pub parmark: bool, +} + +impl Default for Iflags { + fn default() -> Self { + Self { + brkint: false, + icrnl: true, + ignbrk: false, + igncr: false, + ignpar: true, + inlcr: false, + inpck: true, + istrip: false, + ixany: false, + ixoff: false, + ixon: false, + parmark: false, + } + } +} + +#[allow(unused)] +pub struct Oflags { + pub opost: bool, + pub onlcr: bool, + pub ocrnl: bool, + pub onocr: bool, + pub onlret: bool, + pub ofdel: bool, + pub nldly: NlDelay, + pub crdly: CrDelay, + pub tabdly: TabDelay, + pub bsdly: BsDelay, + pub vtdly: VtDelay, + pub ffdly: FfDelay, +} + +impl Default for Oflags { + fn default() -> Self { + Self { + opost: true, + onlcr: true, + ocrnl: false, + onocr: false, + onlret: false, + ofdel: false, + nldly: NlDelay::Nl0, + crdly: CrDelay::Cr0, + tabdly: TabDelay::Tab0, + bsdly: BsDelay::Bs0, + vtdly: VtDelay::Vt0, + ffdly: FfDelay::Ff0, + } + } +} + +#[allow(unused)] +pub enum NlDelay { + Nl0, + Nl1, +} + +#[allow(unused)] +pub enum CrDelay { + Cr0, + Cr1, + Cr2, + Cr3, +} + +#[allow(unused)] +pub enum TabDelay { + Tab0, + Tab1, + Tab2, + Tab3, +} + +#[allow(unused)] +pub enum BsDelay { + Bs0, + Bs1, +} + +#[allow(unused)] +pub enum VtDelay { + Vt0, + Vt1, +} + +#[allow(unused)] +pub enum FfDelay { + Ff0, + Ff1, +} + +#[allow(unused)] +pub struct Cflags { + pub csize: CharSize, + pub cstopb: bool, + pub cread: bool, + pub parenb: bool, + pub parodd: bool, + pub hupcl: bool, + pub clocal: bool, +} + +impl Default for Cflags { + fn default() -> Self { + Self { + csize: CharSize::Cs8, + cstopb: false, + cread: true, + parenb: false, + parodd: false, + hupcl: true, + clocal: false, + } + } +} + +#[allow(unused)] +pub enum CharSize { + Cs5, + Cs6, + Cs7, + Cs8, +} + +#[allow(unused)] +pub struct Lflags { + pub echo: bool, + pub echoe: bool, + pub echok: bool, + pub echonl: bool, + pub icanon: bool, + pub iexten: bool, + pub isig: bool, + pub noflsh: bool, + pub tostop: bool, +} + +impl Default for Lflags { + fn default() -> Self { + Self { + echo: true, + echoe: true, + echok: true, + echonl: false, + icanon: true, + iexten: true, + isig: true, + noflsh: false, + tostop: false, + } + } +} + +#[allow(unused)] +pub struct Termios { + pub iflags: Iflags, + pub oflags: Oflags, + pub cflags: Cflags, + pub lflags: Lflags, + pub veof: char, + pub veol: char, + pub verase: char, + pub vintr: char, + pub vkill: char, + pub vmin: u32, + pub vquit: char, + pub vstart: char, + pub vstop: char, + pub vsusp: char, + pub vtime: u32, +} + +impl Default for Termios { + fn default() -> Self { + Self { + iflags: Iflags::default(), + oflags: Oflags::default(), + cflags: Cflags::default(), + lflags: Lflags::default(), + veof: '\u{4}', //ctrl-D (EOT) + veol: '\n', + verase: '\u{8}', //ctrl-H (BS) + vintr: '\u{3}', //ctrl-C (ETX) + vkill: '\u{15}', //ctrl-U (NAK) + vmin: 0, + vquit: '\u{1C}', //ctrl-\ + vstart: '\u{11}', // ctrl-S (XONN) + vstop: '\u{13}', // ctrl-Q (XOFF) + vsusp: '\u{1A}', // ctrl-Z (SUB) + vtime: 0, + } + } +}