rewrite the stdio module to be less broken. also add sehbang support
This commit is contained in:
@@ -8,4 +8,4 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
crsn = { path = "../crsn" }
|
||||
console = "0.12.0"
|
||||
libc = "0.2.79"
|
||||
|
||||
+159
-30
@@ -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;
|
||||
|
||||
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<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)
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StdioData {
|
||||
console: Term,
|
||||
}
|
||||
|
||||
impl Default for StdioData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
console : Term::stdout()
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user