diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 73d0ea6..d234e13 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -10,6 +10,7 @@ fn next_pair>(tokens: &mut I) -> Option<(TokenTree, } } +#[allow(dead_code)] struct Escape { sequence: String, name: String, diff --git a/src/ext.rs b/src/ext.rs index 762d05a..a2c8c51 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -1,5 +1,66 @@ -mod tail; mod command; +mod tail; +mod which; +mod printenv; +mod echo; -pub use tail::Tail; pub use command::Command; +pub use echo::Echo; +pub use tail::Tail; +pub use which::Which; +pub use printenv::Printenv; + +/* +Posix Shell builtins: + + alias bg cd command false fc fg getopts hash jobs kill + newgrp pwd read true type ulimit umask unalias wait + +Bourne Shell builtins: + + : - do nothing except expand arguments and perform redirections + . - read and execute a file in the current shell context + break - exit from a loop + cd - change the current working directory to directory + continue - resume the next iteration of a loop + eval - evaluate the arguments as a single command + exec - replace the shell without creating a new process + exit - exits the process with a status + export - exports environment variables + getopts - used by shell scripts to parse arguments + hash - displays the hash table used by the shell to map command names to files + pwd - prints the absolute path of the current directory + readonly - mark each name as readonly so that they cannot be reassigned + return - causes a shell function to stop execution and return a value + shift - shifts the position parameters to the left by n + test - evaluate a conditional expression and return a status of 0 or 1 + times - print out the user and system times used by the shell and its children. sorry, what the fuck is this + trap - execute commands when a given signal is received + umask - set the shell process's file creation mask + unset - clears variable or function names + +Bash Builtins: + + alias - creates an alias for a command + bind - adds key bindings + builtin - access a builtin function even if it has been masked by another function + caller - tells you what line number you're executing? + command - runs a command with a given name, ignoring shell functions + declare - declare variables and give them attributes + echo - echos the outputs + enable - enable and disable builtin shell commands + help - display help text about builtin commands + let - perform arithmetic on shell variables + local - create local variables + logout - exits a login shell with a status + mapfile - read lines from stdin or a file into an array + printf - formats arguments using a format string + read - reads one line from stdin or from a file + readarray - read lines from stdin into an array or something + source - executes the script in the current shell + type - tells you if a thing is an alias, function, builtin, or file command + typeset - synonym for declare, included for korn shell compatibility + ulimit - provides control over the resources available to the processes started by the shell + unalias - clears an alias + +*/ diff --git a/src/ext/command.rs b/src/ext/command.rs index 2fcbb3d..8585339 100644 --- a/src/ext/command.rs +++ b/src/ext/command.rs @@ -1,7 +1,7 @@ +use anyhow::Result; + pub trait Command { fn name() -> String; - fn create() -> Self; - - fn exec(&mut self, args: Vec<&str>) -> anyhow::Result; + fn exec(&mut self, args: Vec<&str>) -> Result; } diff --git a/src/ext/echo.rs b/src/ext/echo.rs new file mode 100644 index 0000000..299c747 --- /dev/null +++ b/src/ext/echo.rs @@ -0,0 +1,19 @@ +use crate::ext::Command; +use anyhow::Result; + +pub struct Echo {} + +impl Command for Echo { + fn name() -> String { + String::from("echo") + } + + fn create() -> Self { + Self {} + } + + fn exec(&mut self, args: Vec<&str>) -> Result { + println!("{}", args.join(" ")); + Ok(true) + } +} diff --git a/src/ext/printenv.rs b/src/ext/printenv.rs new file mode 100644 index 0000000..539b159 --- /dev/null +++ b/src/ext/printenv.rs @@ -0,0 +1,33 @@ +use crate::ext::Command; +use anyhow::Result; + +pub struct Printenv {} + +impl Command for Printenv { + fn name() -> String { + String::from("printenv") + } + + fn create() -> Self { + Self {} + } + + fn exec(&mut self, args: Vec<&str>) -> Result { + if args.len() > 0 { + let name = args[0]; + match std::env::var(name) { + Ok(val) => { + println!("{}", val); + Ok(true) + } + Err(e) => { + println!("ERROR {}", e); + Ok(false) + } + } + } else { + println!("which variable you fucking dork"); + Ok(false) + } + } +} diff --git a/src/ext/which.rs b/src/ext/which.rs new file mode 100644 index 0000000..9d9cb35 --- /dev/null +++ b/src/ext/which.rs @@ -0,0 +1,34 @@ +use crate::ext::Command; +use anyhow::Result; + +pub struct Which {} + +impl Command for Which { + fn name() -> String { + String::from("which") + } + + fn create() -> Self { + Self {} + } + + fn exec(&mut self, args: Vec<&str>) -> Result { + if args.len() > 0 { + let path = std::env::var("path").unwrap(); + let dirs: Vec<&str> = path.split(";").collect(); + for d in dirs { + let dir = std::path::Path::new(d); + let fname = dir.join(args[0]).with_extension("exe"); + if fname.exists() && fname.is_file() { + println!("{}", fname.to_str().unwrap()); + return Ok(true); + } + } + println!("not found: {}", args[0]); + Ok(false) + } else { + println!("what do you want to look for?"); + Ok(false) + } + } +} diff --git a/src/main.rs b/src/main.rs index 146def2..ea56fc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod key; mod line; mod log; mod output; +mod parse; mod prompt; mod shell; @@ -60,6 +61,7 @@ fn main() -> Result<()> { shell.output.newline()?; let s = shell.line.pop(); let parts: Vec<&str> = s.split_whitespace().collect(); + if parts.len() > 0 { let cmd = parts[0].to_string(); let args = if parts.len() > 1 { diff --git a/src/output.rs b/src/output.rs index ef880fe..ceddd6d 100644 --- a/src/output.rs +++ b/src/output.rs @@ -102,21 +102,13 @@ impl Writer { pub fn stdout() -> Result { unsafe { let handle = Console::GetStdHandle(Console::STD_OUTPUT_HANDLE) - .context("unable to get stdin handle")?; + .context("unable to get stdout handle")?; let mut stdout = Self{output: handle}; stdout.reset()?; Ok(stdout) } } - // pub fn new() -> Result { - // let mut v = Self { - // output: stdout_handle()?, - // }; - // v.reset()?; - // Ok(v) - // } - pub fn close(&mut self) -> Result<()> { unsafe { CloseHandle(self.output); @@ -128,15 +120,6 @@ impl Writer { unsafe { Console::SetConsoleOutputCP(65001); } - // let mut mode = Console::CONSOLE_MODE(0); - // unsafe { - // if Console::GetConsoleMode(self.output, &mut mode).as_bool() { - // // debug!("Stdout details:"); - // // log_output_mode(mode); - // } else { - // return Err(Error::last_error().into()); - // } - // } Ok(()) } @@ -164,10 +147,10 @@ impl Writer { Ok(()) } - pub fn hide_cursor(&mut self) -> Result<()> { - self.write(b"\x1b[?25l")?; - Ok(()) - } + // pub fn hide_cursor(&mut self) -> Result<()> { + // self.write(b"\x1b[?25l")?; + // Ok(()) + // } } impl Write for Writer { diff --git a/src/parse.rs b/src/parse.rs new file mode 100644 index 0000000..36426a7 --- /dev/null +++ b/src/parse.rs @@ -0,0 +1,131 @@ +#[derive(Debug)] +pub enum Error {} + +#[allow(dead_code)] +#[derive(Debug, PartialEq)] +pub enum Token { + Ident(String), +} + +#[allow(dead_code)] +fn lex>(text: S) -> Result, Error> { + Lexer::new(text.as_ref()).lex() +} + +struct Lexer<'text> { + chars: std::str::Chars<'text>, + peeked: Option, +} + +impl<'text> Lexer<'text> { + fn new(text: &'text str) -> Self { + Self { + chars: text.chars(), + peeked: None, + } + } + + fn lex(&mut self) -> Result, Error> { + let mut tokens = vec![]; + while self.peek().is_some() { + self.skip_whitespace(); + match self.peek() { + None => { return Ok(tokens) }, + Some(_) => { + tokens.push(self.ident()); + } + } + } + Ok(tokens) + } + + fn skip_whitespace(&mut self) { + loop { + match self.peek() { + None => break, + Some(c) => { + if c.is_whitespace() { + self.skip() + } else { + break; + } + } + } + } + } + + fn peek(&mut self) -> Option { + match self.peeked { + Some(c) => Some(c), + None => match self.chars.next() { + Some(c) => { + self.peeked = Some(c); + Some(c) + } + None => None, + }, + } + } + + fn skip(&mut self) { + if self.peeked.is_some() { + self.peeked = None; + } else { + self.chars.next(); + } + } + + fn ident(&mut self) -> Token { + let mut kept: Vec = vec!(); + loop { + match self.peek() { + None => break, + Some(c) => { + if c.is_whitespace() { + break; + } + kept.push(c); + self.skip(); + } + } + } + Token::Ident(kept.iter().collect()) + } +} + +// pub fn parse(line: String) -> Result { +// Ok(Tree::new()) +// } + +// pub struct Tree { +// +// } + +// impl Tree { +// pub fn new() -> Self { Self { } } +// } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lex_empty() -> Result<(), Error> { + let tokens = lex("")?; + assert_eq!(tokens.len(), 0); + Ok(()) + } + + #[test] + fn lex_ident() -> Result<(), Error> { + let tokens = lex("one")?; + assert_eq!(tokens.len(), 1); + assert_eq!(tokens[0], Token::Ident(String::from("one"))); + + let tokens = lex("one two")?; + assert_eq!(tokens.len(), 2); + assert_eq!(tokens[0], Token::Ident(String::from("one"))); + assert_eq!(tokens[1], Token::Ident(String::from("two"))); + Ok(()) + } +} diff --git a/src/shell.rs b/src/shell.rs index af9b1a4..f4eaf07 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,11 +1,14 @@ -use crate::{input, line::Line, log::*, output, ext::{Command, Tail}}; - -use std::{ - fs::File, - io::{Read, Seek, SeekFrom, Write}, - path::{Path, PathBuf}, +use crate::{ + ext::{Command, Echo, Printenv, Tail, Which}, + input, + line::Line, + log::*, + output, + // parse::parse, }; +use std::path::{Path, PathBuf}; + use anyhow::Result; use dirs; @@ -108,48 +111,10 @@ impl Shell { } return Ok(true); } - "printenv" => { - if args.len() > 0 { - let name = args[0]; - match std::env::var(name) { - Ok(val) => { - println!("{}", val); - return Ok(true); - } - Err(e) => { - println!("ERROR {}", e); - return Ok(false); - } - } - } else { - println!("which variable you fucking dork"); - return Ok(false); - } - } - "which" => { - if args.len() > 0 { - let path = std::env::var("path").unwrap(); - let dirs: Vec<&str> = path.split(";").collect(); - for d in dirs { - let dir = std::path::Path::new(d); - let fname = dir.join(args[0]).with_extension("exe"); - if fname.exists() && fname.is_file() { - println!("{}", fname.to_str().unwrap()); - return Ok(true); - } - } - println!("not found: {}", args[0]); - return Ok(false); - } else { - println!("what do you want to look for?"); - return Ok(false); - } - } + "printenv" => Printenv::create().exec(args), + "which" => Which::create().exec(args), "tail" => Tail::create().exec(args), - "echo" => { - println!("{}", args.join(" ")); - return Ok(true); - } + "echo" => Echo::create().exec(args), _ => { let mut proc = std::process::Command::new(cmd); if args.len() > 0 { @@ -171,4 +136,10 @@ impl Shell { } } } + + // pub fn parse(&mut self) -> (String, Vec<&str>) { + // let buf = self.line.show(); + // let tree = parse(buf); + // (String::new(), vec!()) + // } }