|
|
|
@ -6,43 +6,139 @@ use crsn::module::{CrsnExtension, ParseRes, CrsnUniq}; |
|
|
|
|
use crsn::runtime::fault::Fault; |
|
|
|
|
use crsn::runtime::run_thread::{RunState}; |
|
|
|
|
use crsn::sexp::SourcePosition; |
|
|
|
|
use std::io::{Write}; |
|
|
|
|
use std::convert::TryFrom; |
|
|
|
|
use crsn::asm::instr::Cond; |
|
|
|
|
use console::Term; |
|
|
|
|
use std::io; |
|
|
|
|
use crsn::asm::instr::cond::Flag; |
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
|
pub struct StdioOps { |
|
|
|
|
hdl_stdin : Value, |
|
|
|
|
hdl_stdout : Value, |
|
|
|
|
} |
|
|
|
|
mod console { |
|
|
|
|
use std::{io}; |
|
|
|
|
use std::os::unix::io::RawFd; |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct StdioData { |
|
|
|
|
console: Term, |
|
|
|
|
} |
|
|
|
|
use std::ffi::c_void; |
|
|
|
|
use std::mem::{self, MaybeUninit}; |
|
|
|
|
|
|
|
|
|
fn setup_fd(fd: RawFd) -> io::Result<libc::termios> { |
|
|
|
|
use libc::*; |
|
|
|
|
|
|
|
|
|
let mut tio = MaybeUninit::uninit(); |
|
|
|
|
if 0 != unsafe { tcgetattr(fd, tio.as_mut_ptr()) } { |
|
|
|
|
return Err(io::Error::last_os_error()); |
|
|
|
|
} |
|
|
|
|
let mut tio = unsafe { MaybeUninit::assume_init(tio) }; |
|
|
|
|
let old_tio : termios = unsafe { mem::transmute_copy(&tio) }; |
|
|
|
|
|
|
|
|
|
tio.c_iflag &= !(/*BRKINT |*/ /*ICRNL |*/ INPCK | ISTRIP | IXON); |
|
|
|
|
tio.c_oflag |= ONLCR; |
|
|
|
|
tio.c_cflag |= CS8; |
|
|
|
|
tio.c_lflag &= !(ECHO | ICANON | IEXTEN /*| ISIG*/); |
|
|
|
|
tio.c_cc[VMIN] = 1; |
|
|
|
|
tio.c_cc[VTIME] = 0; |
|
|
|
|
if 0 != unsafe { tcsetattr(fd, TCSANOW, &tio) } { |
|
|
|
|
return Err(io::Error::last_os_error()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(old_tio) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Default for StdioData { |
|
|
|
|
fn default() -> Self { |
|
|
|
|
Self { |
|
|
|
|
console : Term::stdout() |
|
|
|
|
pub fn init_io() -> io::Result<libc::termios> { |
|
|
|
|
setup_fd(libc::STDIN_FILENO) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn read_byte() -> io::Result<u8> { |
|
|
|
|
let mut buf = 0u8; |
|
|
|
|
let len = unsafe { libc::read(libc::STDIN_FILENO, &mut buf as *mut u8 as *mut c_void, 1) }; |
|
|
|
|
if len <= 0 { |
|
|
|
|
Err(io::Error::last_os_error()) |
|
|
|
|
} else { |
|
|
|
|
Ok(buf as u8) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn write_byte(b : u8) -> io::Result<()> { |
|
|
|
|
let len = unsafe { libc::write(libc::STDOUT_FILENO, &b as *const u8 as *const c_void, 1) }; |
|
|
|
|
if len <= 0 { |
|
|
|
|
Err(io::Error::last_os_error()) |
|
|
|
|
} else { |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn write_char(c : char) -> io::Result<()> { |
|
|
|
|
let mut buf = [0u8; 4]; |
|
|
|
|
for b in c.encode_utf8(&mut buf).as_bytes() { |
|
|
|
|
write_byte(*b)?; |
|
|
|
|
} |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn read_char() -> io::Result<char> { |
|
|
|
|
let first = read_byte()?; |
|
|
|
|
|
|
|
|
|
if first & 0x80 == 0 { |
|
|
|
|
return Ok(first as char); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut bytes = [first, 0, 0, 0]; |
|
|
|
|
|
|
|
|
|
let remain = if first & 0b1110_0000 == 0b1100_0000 { |
|
|
|
|
1 |
|
|
|
|
} else if first & 0b1111_0000 == 0b1110_0000 { |
|
|
|
|
2 |
|
|
|
|
} else /*if first & 0b1111_1000 == 0b1111_0000*/ { |
|
|
|
|
3 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
for n in 1..=remain { |
|
|
|
|
bytes[n] = read_byte()?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::str::from_utf8(&bytes[..=remain]) |
|
|
|
|
.map(|s| s.chars().nth(0).unwrap()) |
|
|
|
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
|
pub struct StdioOps { |
|
|
|
|
old_tio: Option<libc::termios>, |
|
|
|
|
hdl_stdin : Value, |
|
|
|
|
hdl_stdin_raw : Value, |
|
|
|
|
hdl_stdout : Value, |
|
|
|
|
hdl_stdout_raw : Value, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl StdioOps { |
|
|
|
|
pub fn new() -> Box<dyn CrsnExtension> { |
|
|
|
|
Box::new(Self { |
|
|
|
|
old_tio: None, |
|
|
|
|
hdl_stdin: 0, |
|
|
|
|
hdl_stdout: 0 |
|
|
|
|
hdl_stdin_raw: 0, |
|
|
|
|
hdl_stdout: 0, |
|
|
|
|
hdl_stdout_raw: 0, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Drop for StdioOps { |
|
|
|
|
fn drop(&mut self) { |
|
|
|
|
// Un-break the terminal
|
|
|
|
|
if let Some(tio) = self.old_tio.take() { |
|
|
|
|
let _ = unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &tio) }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl CrsnExtension for StdioOps { |
|
|
|
|
fn init(&mut self, uniq: &CrsnUniq) { |
|
|
|
|
self.hdl_stdin = uniq.unique_handle(); |
|
|
|
|
self.hdl_stdin_raw = uniq.unique_handle(); |
|
|
|
|
self.hdl_stdout = uniq.unique_handle(); |
|
|
|
|
self.hdl_stdout_raw = uniq.unique_handle(); |
|
|
|
|
|
|
|
|
|
// This can fail if the input is not a tty
|
|
|
|
|
if let Ok(tio) = console::init_io() { |
|
|
|
|
self.old_tio = Some(tio); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get value of an extension-provided constant.
|
|
|
|
@ -50,8 +146,10 @@ impl CrsnExtension for StdioOps { |
|
|
|
|
fn get_constant_value<'a>(&self, name: &str) -> Option<Value> |
|
|
|
|
{ |
|
|
|
|
match name { |
|
|
|
|
"stdin" => Some(self.hdl_stdin), |
|
|
|
|
"stdout" => Some(self.hdl_stdout), |
|
|
|
|
"cin" => Some(self.hdl_stdin), |
|
|
|
|
"cin_r" => Some(self.hdl_stdin_raw), |
|
|
|
|
"cout" => Some(self.hdl_stdout), |
|
|
|
|
"cout_r" => Some(self.hdl_stdout_raw), |
|
|
|
|
_ => None |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -69,12 +167,36 @@ impl CrsnExtension for StdioOps { |
|
|
|
|
-> Result<Option<Value>, Fault> |
|
|
|
|
{ |
|
|
|
|
if handle == self.hdl_stdin { |
|
|
|
|
let data = state.ext_mut::<StdioData>(); |
|
|
|
|
return Ok(Some(data.console.read_char().expect("stdin read") as u64)); |
|
|
|
|
match console::read_char() { |
|
|
|
|
Ok(c) => { |
|
|
|
|
return Ok(Some(c as Value)); |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
if e.kind() != io::ErrorKind::InvalidData { |
|
|
|
|
state.set_flag(Flag::Eof, true); |
|
|
|
|
} |
|
|
|
|
return Ok(Some(0)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if handle == self.hdl_stdout { |
|
|
|
|
return Err(Fault::NotAllowed("Cannot read stdout".into())); |
|
|
|
|
if handle == self.hdl_stdin_raw { |
|
|
|
|
match console::read_byte() { |
|
|
|
|
Ok(b) => { |
|
|
|
|
return Ok(Some(b as Value)); |
|
|
|
|
} |
|
|
|
|
Err(_e) => { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
state.set_flag(Flag::Eof, true); |
|
|
|
|
return Ok(Some(0)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if handle == self.hdl_stdout || handle == self.hdl_stdout_raw { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
return Ok(Some(0)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(None) |
|
|
|
@ -87,18 +209,25 @@ impl CrsnExtension for StdioOps { |
|
|
|
|
|
|
|
|
|
if handle == self.hdl_stdout { |
|
|
|
|
if let Ok(a_char) = char::try_from((value & 0xFFFF_FFFF) as u32) { |
|
|
|
|
let data = state.ext_mut::<StdioData>(); |
|
|
|
|
|
|
|
|
|
let mut b = [0; 4]; |
|
|
|
|
data.console.write(a_char.encode_utf8(&mut b).as_bytes()).expect("stdout write"); |
|
|
|
|
if console::write_char(a_char).is_err() { |
|
|
|
|
state.set_flag(Flag::Eof, true); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
state.set_flag(Cond::Invalid, true); |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
} |
|
|
|
|
return Ok(Some(())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if handle == self.hdl_stdin { |
|
|
|
|
return Err(Fault::NotAllowed("Cannot write stdin".into())); |
|
|
|
|
if handle == self.hdl_stdout_raw { |
|
|
|
|
if console::write_byte((value & 0xFF) as u8).is_err() { |
|
|
|
|
state.set_flag(Flag::Eof, true); |
|
|
|
|
} |
|
|
|
|
return Ok(Some(())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if handle == self.hdl_stdin || handle == self.hdl_stdin_raw { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
return Ok(Some(())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(None) |
|
|
|
|