Add comments and ctrl+c not exiting
This commit is contained in:
parent
ea6c94a584
commit
41c3ffcf54
3 changed files with 131 additions and 60 deletions
|
@ -5,6 +5,7 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.37", features = ["derive", "env"] }
|
||||
ctrlc = "3.4.6"
|
||||
hostname = "0.4.1"
|
||||
users = "0.11.0"
|
||||
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
#![allow(clippy::type_complexity)]
|
||||
|
||||
/// List of builtins
|
||||
pub const BUILTINS: [(&str, fn (args: Vec<String>, state: &mut super::State) -> i32); 2] = [("cd", cd), ("exit", exit)];
|
||||
pub const BUILTINS: [(&str, fn (args: Vec<String>, unsplit_args: String, state: &mut super::State) -> i32); 3] = [("cd", cd), ("exit", exit), ("echo", echo)];
|
||||
|
||||
/// Change the directory
|
||||
pub fn cd(args: Vec<String>, state: &mut super::State) -> i32 {
|
||||
pub fn cd(args: Vec<String>, unsplit_args: String, state: &mut super::State) -> i32 {
|
||||
if args.len() == 1 {
|
||||
state.working_dir = std::env::home_dir().unwrap();
|
||||
return 0;
|
||||
}
|
||||
if args[1] == ".." {
|
||||
state.working_dir.pop();
|
||||
return 0;
|
||||
|
@ -15,6 +19,22 @@ pub fn cd(args: Vec<String>, state: &mut super::State) -> i32 {
|
|||
}
|
||||
|
||||
/// Exit the shell
|
||||
pub fn exit(_: Vec<String>, _: &mut super::State) -> i32 {
|
||||
pub fn exit(_: Vec<String>, _: String, _: &mut super::State) -> i32 {
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
/// Echo a string
|
||||
pub fn echo(args: Vec<String>, mut unsplit_args: String, _: &mut super::State) -> i32 {
|
||||
unsplit_args = unsplit_args[5..].to_string();
|
||||
if args.len() != 1 && args[1] == "-e" {
|
||||
unsplit_args = unsplit_args[3..].to_string();
|
||||
let escaped = crate::escapes::interpret_escaped_string(&unsplit_args);
|
||||
if escaped.is_err() {
|
||||
println!("sesh: echo: invalid escape: {}", escaped.unwrap_err());
|
||||
return 1;
|
||||
}
|
||||
unsplit_args = escaped.unwrap();
|
||||
}
|
||||
println!("{}", unsplit_args);
|
||||
0
|
||||
}
|
||||
|
|
124
src/main.rs
124
src/main.rs
|
@ -5,10 +5,9 @@
|
|||
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
io::Write,
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::Mutex,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
|
@ -53,7 +52,7 @@ enum Variable {
|
|||
#[derive(Clone, Debug)]
|
||||
struct State {
|
||||
/// Environment variables
|
||||
env: Rc<Mutex<std::env::VarsOs>>,
|
||||
env: Arc<Mutex<std::env::VarsOs>>,
|
||||
/// Shell-local variables only accessible via builtins.
|
||||
shell_env: ShellVars,
|
||||
/// The focused variable.
|
||||
|
@ -64,6 +63,10 @@ struct State {
|
|||
working_dir: PathBuf,
|
||||
}
|
||||
|
||||
unsafe impl Sync for State {}
|
||||
unsafe impl Send for State {}
|
||||
|
||||
/// Split a statement.
|
||||
fn split_statement(statement: &str) -> Vec<String> {
|
||||
let mut out = vec![String::new()];
|
||||
let mut i: usize = 0;
|
||||
|
@ -91,32 +94,50 @@ fn split_statement(statement: &str) -> Vec<String> {
|
|||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
/// Removes comments from a statement
|
||||
fn remove_comments(statement: &str) -> String {
|
||||
let mut out = String::new();
|
||||
let mut in_comment = false;
|
||||
for ch in statement.chars() {
|
||||
if in_comment {
|
||||
if ch == '\n' {
|
||||
out.push(ch);
|
||||
in_comment = false
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if ch == '#' {
|
||||
in_comment = true;
|
||||
continue;
|
||||
}
|
||||
out.push(ch);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
/// Evaluate a statement. May include multiple.
|
||||
fn eval(statement: &str, state: &mut State) {
|
||||
let statement = escapes::interpret_escaped_string(statement);
|
||||
if statement.is_err() {
|
||||
println!("sesh: invalid escape: {}", statement.unwrap_err());
|
||||
return;
|
||||
}
|
||||
let statement = remove_comments(statement);
|
||||
let statements = statement
|
||||
.unwrap()
|
||||
.split("\n")
|
||||
.map(|val| val.split(";").collect::<Vec<&str>>())
|
||||
.collect::<Vec<Vec<&str>>>()
|
||||
.iter()
|
||||
.map(|val| val.iter().map(|val| val.trim()).collect::<Vec<&str>>())
|
||||
.collect::<Vec<Vec<&str>>>()
|
||||
.concat()
|
||||
.iter()
|
||||
.map(|val| split_statement(val))
|
||||
.collect::<Vec<Vec<String>>>();
|
||||
.concat();
|
||||
|
||||
for statement in statements {
|
||||
if statement.is_empty() || statement[0].is_empty() {
|
||||
let statement_split = split_statement(statement);
|
||||
if statement.is_empty() || statement_split[0].is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some(builtin) = builtins::BUILTINS.iter().find(|v| v.0 == statement[0]) {
|
||||
let status = builtin.1(statement, state);
|
||||
if let Some(builtin) = builtins::BUILTINS
|
||||
.iter()
|
||||
.find(|v| v.0 == statement_split[0])
|
||||
{
|
||||
let status = builtin.1(statement_split, statement.to_string(), state);
|
||||
for (i, var) in state.shell_env.clone().into_iter().enumerate() {
|
||||
if var.name == "STATUS" {
|
||||
state.shell_env.swap_remove(i);
|
||||
|
@ -129,8 +150,8 @@ fn eval(statement: &str, state: &mut State) {
|
|||
});
|
||||
continue;
|
||||
}
|
||||
match std::process::Command::new(statement[0].clone())
|
||||
.args(&statement[1..])
|
||||
match std::process::Command::new(statement_split[0].clone())
|
||||
.args(&statement_split[1..])
|
||||
.current_dir(state.working_dir.clone())
|
||||
.spawn()
|
||||
{
|
||||
|
@ -154,25 +175,13 @@ fn eval(statement: &str, state: &mut State) {
|
|||
}
|
||||
}
|
||||
|
||||
state.env = Rc::new(Mutex::new(std::env::vars_os()));
|
||||
state.history.push(state.clone());
|
||||
state.env = Arc::new(Mutex::new(std::env::vars_os()));
|
||||
let s = state.clone();
|
||||
state.history.push(s);
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let interactive = true;
|
||||
let mut state = State {
|
||||
env: Rc::new(Mutex::new(std::env::vars_os())),
|
||||
shell_env: Vec::new(),
|
||||
focus: Variable::Local(String::new()),
|
||||
history: Vec::new(),
|
||||
working_dir: std::env::current_dir()
|
||||
.unwrap_or(std::env::home_dir().unwrap_or(PathBuf::from("/"))),
|
||||
};
|
||||
state.shell_env.push(ShellVar {
|
||||
name: "PROMPT".to_string(),
|
||||
value: "$u@$h $P> ".to_string(),
|
||||
});
|
||||
loop {
|
||||
/// Write the prompt to the screen.
|
||||
fn write_prompt(state: State) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut prompt = state
|
||||
.shell_env
|
||||
.iter()
|
||||
|
@ -206,9 +215,50 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
print!("{}", prompt);
|
||||
std::io::stdout().flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut state = State {
|
||||
env: Arc::new(Mutex::new(std::env::vars_os())),
|
||||
shell_env: Vec::new(),
|
||||
focus: Variable::Local(String::new()),
|
||||
history: Vec::new(),
|
||||
working_dir: std::env::current_dir()
|
||||
.unwrap_or(std::env::home_dir().unwrap_or(PathBuf::from("/"))),
|
||||
};
|
||||
state.shell_env.push(ShellVar {
|
||||
name: "PROMPT".to_string(),
|
||||
value: "\x1b[32m$u@$h\x1b[39m \x1b[34m$P\x1b[39m> ".to_string(),
|
||||
});
|
||||
|
||||
let ctrlc_cont = Arc::new(RwLock::new(false));
|
||||
let cc2 = ctrlc_cont.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
(*cc2.write().unwrap()) = true;
|
||||
})
|
||||
.expect("Error setting Ctrl-C handler");
|
||||
'mainloop: loop {
|
||||
write_prompt(state.clone())?;
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input)?;
|
||||
|
||||
let mut i0 = [0u8];
|
||||
while i0[0] != b'\n' {
|
||||
if ctrlc_cont.read().unwrap().to_owned() {
|
||||
input.clear();
|
||||
(*ctrlc_cont.write().unwrap()) = false;
|
||||
println!();
|
||||
continue 'mainloop;
|
||||
}
|
||||
let amount = std::io::stdin().read(&mut i0).unwrap();
|
||||
if amount == 0 {
|
||||
continue;
|
||||
}
|
||||
input.push(char::from_u32(i0[0] as u32).unwrap());
|
||||
}
|
||||
|
||||
eval(&input, &mut state);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue