summary refs log tree commit diff
diff options
context:
space:
mode:
authorIrene Knapp <ireneista@gmail.com>2021-01-16 03:05:28 -0800
committerIrene Knapp <ireneista@gmail.com>2021-01-16 03:05:28 -0800
commit380db764e0c5466f1564045c7da40fdde967612c (patch)
tree1007d0d9501ae1d359a2295289fb9bfd431785dd
parent031c033745060fc2c83db5a2bf63fd1942ad3176 (diff)
put the terminal in raw mode; also add a TerminalError type
-rw-r--r--Cargo.lock41
-rw-r--r--Cargo.toml1
-rw-r--r--src/error.rs9
-rw-r--r--src/main.rs2
-rw-r--r--src/terminal.rs34
-rw-r--r--src/terminal/error.rs31
6 files changed, 107 insertions, 11 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4f2703d..c775fd3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -69,6 +69,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
 
 [[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
 name = "blake2b_simd"
 version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -113,12 +119,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 
 [[package]]
+name = "cc"
+version = "1.0.66"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
+
+[[package]]
 name = "cfg-if"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
 [[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
 name = "constant_time_eq"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -131,7 +149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
 dependencies = [
  "autocfg",
- "cfg-if",
+ "cfg-if 0.1.10",
  "lazy_static",
 ]
 
@@ -215,7 +233,7 @@ version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
  "libc",
  "wasi",
 ]
@@ -296,9 +314,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.76"
+version = "0.2.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
+checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
 
 [[package]]
 name = "log"
@@ -306,7 +324,7 @@ version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
 dependencies = [
- "cfg-if",
+ "cfg-if 0.1.10",
 ]
 
 [[package]]
@@ -322,6 +340,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
 
 [[package]]
+name = "nix"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 1.0.0",
+ "libc",
+]
+
+[[package]]
 name = "opaque-debug"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -455,6 +485,7 @@ version = "0.1.0"
 dependencies = [
  "lalrpop",
  "lalrpop-util",
+ "nix",
 ]
 
 [[package]]
diff --git a/Cargo.toml b/Cargo.toml
index a0470e6..f858223 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2018"
 [dependencies]
 #futures = "0.3"
 lalrpop-util = "0.19"
+nix = "0.19"
 
 [build-dependencies]
 lalrpop = { version = "0.19", features = [ "lexer" ] }
diff --git a/src/error.rs b/src/error.rs
index 018d812..b8289f6 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,5 +1,6 @@
 use crate::path::GenericPath;
 use crate::path::error::{FileNameError, DirectoryNameError};
+use crate::terminal::error::TerminalError;
 
 
 type ParseError<'a> =
@@ -16,6 +17,7 @@ pub enum Error {
   PathLexicallyRelative(GenericPath),
   PathLexicallyInvalid(GenericPath),
   PathEmpiricallyFile(GenericPath),
+  Terminal(TerminalError),
 }
 
 impl std::error::Error for Error { }
@@ -48,6 +50,7 @@ impl std::fmt::Display for Error {
         f.write_fmt(format_args!(
             "There's a file at {}, not a directory.",
             path)),
+      Error::Terminal(e) => e.fmt(f),
     }
   }
 }
@@ -81,3 +84,9 @@ impl From<DirectoryNameError> for Error {
     Error::DirectoryName(e)
   }
 }
+
+impl From<TerminalError> for Error {
+  fn from(e: TerminalError) -> Error {
+    Error::Terminal(e)
+  }
+}
diff --git a/src/main.rs b/src/main.rs
index 3199528..d7a2aff 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -39,7 +39,7 @@ fn repl() -> Result<()> {
   loop {
     prompt()?;
 
-    terminal::handle_input(io::stdin())?;
+    terminal::handle_input_terminal(io::stdin())?;
     let input = read()?;
     match input {
       Input::String(string) => execute(&string)?,
diff --git a/src/terminal.rs b/src/terminal.rs
index 6d65c82..27e3c68 100644
--- a/src/terminal.rs
+++ b/src/terminal.rs
@@ -1,6 +1,12 @@
-use std::io::{self, BufRead, BufReader, Read};
+use crate::terminal::error::{TerminalError};
+
+use nix::sys::termios;
+use std::io::{BufRead, BufReader, Read};
+use std::os::unix::io::AsRawFd;
 use std::str;
 
+pub mod error;
+
 
 struct CharBufReader<R: Read> {
   reader: BufReader<R>,
@@ -17,10 +23,10 @@ impl<R: Read> CharBufReader<R> {
   }
 
   pub fn fill_buf(&mut self)
-    -> std::result::Result<&str, io::Error>
+    -> std::result::Result<&str, TerminalError>
   {
     loop {
-      let byte_buffer = self.reader.fill_buf()?;
+      let byte_buffer = self.reader.fill_buf().map_err(error::input)?;
 
       match str::from_utf8(byte_buffer) {
         Err(error) => {
@@ -63,13 +69,31 @@ impl<R: Read> CharBufReader<R> {
 }
 
 
+pub fn handle_input_terminal(input_stream: impl Read + AsRawFd)
+  -> std::result::Result<(), TerminalError>
+{
+  let fd = input_stream.as_raw_fd();
+  let mut termios = termios::tcgetattr(fd)
+      .map_err(error::mode_setting)?;
+  termios.local_flags.insert(termios::LocalFlags::ECHO);
+  termios.local_flags.remove(termios::LocalFlags::ECHOCTL);
+  termios.local_flags.remove(termios::LocalFlags::ICANON);
+  termios.control_chars[termios::SpecialCharacterIndices::VMIN as usize] = 1;
+  termios.control_chars[termios::SpecialCharacterIndices::VTIME as usize] = 0;
+  termios::tcsetattr(fd, termios::SetArg::TCSANOW, &termios)
+      .map_err(error::mode_setting)?;
+
+  handle_input(input_stream)
+}
+
+
 pub fn handle_input(input_stream: impl Read)
-  -> std::result::Result<(), io::Error>
+  -> std::result::Result<(), TerminalError>
 {
   let reader = BufReader::new(input_stream);
   let mut reader = CharBufReader::new(reader);
 
-  let string = reader.fill_buf()?;
+  let string = reader.fill_buf().map_err(error::input)?;
   println!("top level {:?}", string);
 
   let n_to_consume = string.len();
diff --git a/src/terminal/error.rs b/src/terminal/error.rs
new file mode 100644
index 0000000..6666e49
--- /dev/null
+++ b/src/terminal/error.rs
@@ -0,0 +1,31 @@
+#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)]
+pub enum TerminalError {
+  Input(String),
+  ModeSetting(String),
+}
+
+impl std::error::Error for TerminalError { }
+
+impl std::fmt::Display for TerminalError {
+  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    match self {
+      TerminalError::Input(s) =>
+        f.write_fmt(format_args!(
+            "Can't read terminal input: {}", s)),
+      TerminalError::ModeSetting(s) =>
+        f.write_fmt(format_args!(
+            "Can't set terminal mode: {}", s)),
+    }
+  }
+}
+
+
+pub fn input(e: impl std::error::Error) -> TerminalError {
+  TerminalError::ModeSetting(format!("{}", e))
+}
+
+
+pub fn mode_setting(e: impl std::error::Error) -> TerminalError {
+  TerminalError::ModeSetting(format!("{}", e))
+}
+