use crsn::asm::data::literal::Value; use crsn::asm::error::CrsnError; use crsn::asm::instr::op::OpKind; use crsn::asm::parse::arg_parser::TokenParser; use crsn::module::{CrsnExtension, ParseRes, CrsnUniq}; use crsn::runtime::fault::Fault; use crsn::runtime::run_thread::{RunState}; use crsn::sexp::SourcePosition; use std::convert::TryFrom; use std::io; use crsn::asm::instr::cond::Flag; use std::fmt; mod console { use std::{io}; use std::os::unix::io::RawFd; use std::ffi::c_void; use std::mem::{self, MaybeUninit}; fn setup_fd(fd: RawFd) -> io::Result { 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) } pub fn init_io() -> io::Result { setup_fd(libc::STDIN_FILENO) } pub fn read_byte() -> io::Result { 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 { 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(Clone)] pub struct StdioOps { old_tio: Option, hdl_stdin : Value, hdl_stdin_raw : Value, hdl_stdout : Value, hdl_stdout_raw : Value, } impl fmt::Debug for StdioOps { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StdioOps") .field("hdl_stdin", &format_args!("0x{:08x}", self.hdl_stdin)) .field("hdl_stdin_raw", &format_args!("0x{:08x}", self.hdl_stdin_raw)) .field("hdl_stdout", &format_args!("0x{:08x}", self.hdl_stdout)) .field("hdl_stdout_raw", &format_args!("0x{:08x}", self.hdl_stdout_raw)) .finish() } } impl StdioOps { pub fn new() -> Box { Box::new(Self { old_tio: None, hdl_stdin: 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. /// This constant may be an object handle, or a constant value used as argument in some other instruction. fn get_constant_value<'a>(&self, name: &str) -> Option { match name { "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 } } fn name(&self) -> &'static str { "stdio" } fn parse_op<'a>(&self, _pos: &SourcePosition, _keyword: &str, args: TokenParser<'a>) -> Result, CrsnError> { Ok(ParseRes::Unknown(args)) } /// Run-time method called to read an object (using the object handle syntax) fn read_obj(&self, state: &mut RunState, handle: Value) -> Result, Fault> { if handle == self.hdl_stdin { 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_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) } /// Run-time method called to write an object (using the object handle syntax) fn write_obj(&self, state: &mut RunState, handle: Value, value: Value) -> Result, Fault> { state.clear_status(); if handle == self.hdl_stdout { if let Ok(a_char) = char::try_from((value & 0xFFFF_FFFF) as u32) { if console::write_char(a_char).is_err() { state.set_flag(Flag::Eof, true); } } else { state.set_flag(Flag::Invalid, true); } return Ok(Some(())); } 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) } }