Progress
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn alias(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs: _ } = rule {
|
||||
let argv = argv.drop_first();
|
||||
let mut argv_iter = argv.into_iter();
|
||||
while let Some(arg) = argv_iter.next() {
|
||||
let arg_raw = shenv.input_slice(arg.span()).to_string();
|
||||
if let Some((alias,body)) = arg_raw.split_once('=') {
|
||||
let clean_body = clean_string(&body);
|
||||
shenv.logic_mut().set_alias(alias, &clean_body);
|
||||
} else {
|
||||
return Err(ShErr::full(ShErrKind::SyntaxErr, "Expected an assignment in alias args", shenv.get_input(), arg.span().clone()))
|
||||
}
|
||||
}
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn cd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs: _ } = rule {
|
||||
let mut argv_iter = argv.into_iter();
|
||||
argv_iter.next(); // Ignore 'cd'
|
||||
let dir_raw = argv_iter.next().map(|arg| shenv.input_slice(arg.span()).into()).unwrap_or(std::env::var("HOME")?);
|
||||
let dir = PathBuf::from(&dir_raw);
|
||||
std::env::set_current_dir(dir)?;
|
||||
let new_dir = std::env::current_dir()?;
|
||||
shenv.vars_mut().export("PWD",new_dir.to_str().unwrap());
|
||||
shenv.set_code(0);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn sh_flow(node: Node, shenv: &mut ShEnv, kind: ShErrKind) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
let mut code: i32 = 0;
|
||||
if let NdRule::Command { argv, redirs } = rule {
|
||||
let mut argv_iter = argv.into_iter();
|
||||
while let Some(arg) = argv_iter.next() {
|
||||
if let Ok(code_arg) = shenv.input_slice(arg.span()).parse() {
|
||||
code = code_arg
|
||||
}
|
||||
}
|
||||
} else { unreachable!() }
|
||||
shenv.set_code(code);
|
||||
// Our control flow keywords are used as ShErrKinds
|
||||
// This design will halt the execution flow and start heading straight back upward
|
||||
// Function returns and loop breaks/continues will be caught in the proper context to allow
|
||||
// Execution to continue at the proper return point.
|
||||
Err(ShErr::simple(kind, ""))
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
use shellenv::jobs::{ChildProc, JobBldr};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
pub struct EchoFlags: u32 {
|
||||
const USE_ESCAPE = 0b0001;
|
||||
const NO_ESCAPE = 0b0010;
|
||||
const STDERR = 0b0100;
|
||||
const NO_NEWLINE = 0b1000;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn echo(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
|
||||
if let NdRule::Command { argv, redirs } = rule {
|
||||
let mut argv_iter = argv.into_iter().skip(1).peekable();
|
||||
let mut echo_flags = EchoFlags::empty();
|
||||
while let Some(arg) = argv_iter.peek() {
|
||||
let blame = arg.span();
|
||||
let raw = arg.as_raw(shenv);
|
||||
if raw.starts_with('-') {
|
||||
let _ = argv_iter.next();
|
||||
let mut options = raw.strip_prefix('-').unwrap().chars();
|
||||
while let Some(opt) = options.next() {
|
||||
match opt {
|
||||
'r' => echo_flags |= EchoFlags::STDERR,
|
||||
'n' => echo_flags |= EchoFlags::NO_NEWLINE,
|
||||
'e' => {
|
||||
if echo_flags.contains(EchoFlags::NO_ESCAPE) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
"the 'e' and 'E' flags are mutually exclusive",
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
echo_flags |= EchoFlags::USE_ESCAPE;
|
||||
}
|
||||
'E' => {
|
||||
if echo_flags.contains(EchoFlags::USE_ESCAPE) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
"the 'e' and 'E' flags are mutually exclusive",
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
echo_flags |= EchoFlags::NO_ESCAPE;
|
||||
}
|
||||
_ => return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
format!("Unrecognized echo option"),
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
let mut argv = argv_iter.collect::<Vec<_>>().as_strings(shenv);
|
||||
argv.retain(|arg| arg != "\n");
|
||||
log!(DEBUG,argv);
|
||||
let mut formatted = argv.join(" ");
|
||||
if !echo_flags.contains(EchoFlags::NO_NEWLINE) {
|
||||
formatted.push('\n');
|
||||
}
|
||||
|
||||
shenv.collect_redirs(redirs);
|
||||
log!(DEBUG,"{:?}",shenv.ctx().redirs());
|
||||
shenv.ctx_mut().activate_rdrs()?;
|
||||
write_out(formatted)?;
|
||||
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn export(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs: _ } = rule {
|
||||
let mut argv_iter = argv.into_iter();
|
||||
argv_iter.next(); // Ignore 'export'
|
||||
while let Some(arg) = argv_iter.next() {
|
||||
let arg_raw = arg.as_raw(shenv);
|
||||
if let Some((var,val)) = arg_raw.split_once('=') {
|
||||
shenv.vars_mut().export(var, &clean_string(val));
|
||||
} else {
|
||||
eprintln!("Expected an assignment in export args, found this: {}", arg_raw)
|
||||
}
|
||||
}
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
use shellenv::jobs::JobCmdFlags;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn continue_job(node: Node, shenv: &mut ShEnv, fg: bool) -> ShResult<()> {
|
||||
let blame = node.span();
|
||||
let cmd = if fg { "fg" } else { "bg" };
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs } = rule {
|
||||
let mut argv_s = argv.drop_first().as_strings(shenv).into_iter();
|
||||
|
||||
if read_jobs(|j| j.get_fg().is_some()) {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::InternalErr,
|
||||
format!("Somehow called {} with an existing foreground job",cmd),
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let curr_job_id = if let Some(id) = read_jobs(|j| j.curr_job()) {
|
||||
id
|
||||
} else {
|
||||
return Err(ShErr::full(ShErrKind::ExecFail, "No jobs found", shenv.get_input(), blame))
|
||||
};
|
||||
|
||||
let tabid = match argv_s.next() {
|
||||
Some(arg) => parse_job_id(&arg, blame.clone(),shenv)?,
|
||||
None => curr_job_id
|
||||
};
|
||||
|
||||
let mut job = write_jobs(|j| {
|
||||
let id = JobID::TableID(tabid);
|
||||
let query_result = j.query(id.clone());
|
||||
if query_result.is_some() {
|
||||
Ok(j.remove_job(id.clone()).unwrap())
|
||||
} else {
|
||||
Err(
|
||||
ShErr::full(
|
||||
ShErrKind::ExecFail,
|
||||
format!("Job id `{}' not found", tabid),
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
})?;
|
||||
|
||||
job.killpg(Signal::SIGCONT)?;
|
||||
|
||||
if fg {
|
||||
write_jobs(|j| j.new_fg(job))?;
|
||||
} else {
|
||||
let job_order = read_jobs(|j| j.order().to_vec());
|
||||
write(borrow_fd(1), job.display(&job_order, JobCmdFlags::PIDS).as_bytes())?;
|
||||
write_jobs(|j| j.insert_job(job, true))?;
|
||||
}
|
||||
shenv.set_code(0);
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_job_id(arg: &str, blame: Rc<RefCell<Span>>, shenv: &mut ShEnv) -> ShResult<usize> {
|
||||
if arg.starts_with('%') {
|
||||
let arg = arg.strip_prefix('%').unwrap();
|
||||
if arg.chars().all(|ch| ch.is_ascii_digit()) {
|
||||
Ok(arg.parse::<usize>().unwrap())
|
||||
} else {
|
||||
let result = write_jobs(|j| {
|
||||
let query_result = j.query(JobID::Command(arg.into()));
|
||||
query_result.map(|job| job.tabid().unwrap())
|
||||
});
|
||||
match result {
|
||||
Some(id) => Ok(id),
|
||||
None => Err(
|
||||
ShErr::full(
|
||||
ShErrKind::InternalErr,
|
||||
"Found a job but no table id in parse_job_id()",
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if arg.chars().all(|ch| ch.is_ascii_digit()) {
|
||||
let result = write_jobs(|j| {
|
||||
let pgid_query_result = j.query(JobID::Pgid(Pid::from_raw(arg.parse::<i32>().unwrap())));
|
||||
if let Some(job) = pgid_query_result {
|
||||
return Some(job.tabid().unwrap())
|
||||
}
|
||||
|
||||
if arg.parse::<i32>().unwrap() > 0 {
|
||||
let table_id_query_result = j.query(JobID::TableID(arg.parse::<usize>().unwrap()));
|
||||
return table_id_query_result.map(|job| job.tabid().unwrap());
|
||||
}
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
match result {
|
||||
Some(id) => Ok(id),
|
||||
None => Err(
|
||||
ShErr::full(
|
||||
ShErrKind::InternalErr,
|
||||
"Found a job but no table id in parse_job_id()",
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Err(
|
||||
ShErr::full(
|
||||
ShErrKind::SyntaxErr,
|
||||
format!("Invalid fd arg: {}", arg),
|
||||
shenv.get_input(),
|
||||
blame
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jobs(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs } = rule {
|
||||
let mut argv = argv.drop_first().into_iter();
|
||||
|
||||
let mut flags = JobCmdFlags::empty();
|
||||
while let Some(arg) = argv.next() {
|
||||
let arg_s = shenv.input_slice(arg.span());
|
||||
let mut chars = arg_s.chars().peekable();
|
||||
if chars.peek().is_none_or(|ch| *ch != '-') {
|
||||
return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::SyntaxErr,
|
||||
"Invalid flag in jobs call",
|
||||
shenv.get_input(),
|
||||
arg.span()
|
||||
)
|
||||
)
|
||||
}
|
||||
chars.next();
|
||||
while let Some(ch) = chars.next() {
|
||||
let flag = match ch {
|
||||
'l' => JobCmdFlags::LONG,
|
||||
'p' => JobCmdFlags::PIDS,
|
||||
'n' => JobCmdFlags::NEW_ONLY,
|
||||
'r' => JobCmdFlags::RUNNING,
|
||||
's' => JobCmdFlags::STOPPED,
|
||||
_ => return Err(
|
||||
ShErr::full(
|
||||
ShErrKind::SyntaxErr,
|
||||
"Invalid flag in jobs call",
|
||||
shenv.get_input(),
|
||||
arg.span()
|
||||
)
|
||||
)
|
||||
|
||||
};
|
||||
flags |= flag
|
||||
}
|
||||
}
|
||||
write_jobs(|j| j.print_jobs(flags))?;
|
||||
shenv.set_code(0);
|
||||
} else { unreachable!() }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
pub mod echo;
|
||||
pub mod cd;
|
||||
pub mod pwd;
|
||||
pub mod export;
|
||||
pub mod jobctl;
|
||||
pub mod read;
|
||||
pub mod alias;
|
||||
pub mod control_flow;
|
||||
pub mod source;
|
||||
|
||||
pub const BUILTINS: [&str;14] = [
|
||||
"echo",
|
||||
"cd",
|
||||
"pwd",
|
||||
"export",
|
||||
"fg",
|
||||
"bg",
|
||||
"jobs",
|
||||
"read",
|
||||
"alias",
|
||||
"exit",
|
||||
"continue",
|
||||
"return",
|
||||
"break",
|
||||
"source",
|
||||
];
|
||||
@@ -1,17 +0,0 @@
|
||||
use shellenv::jobs::{ChildProc, JobBldr};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn pwd(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv: _, redirs } = rule {
|
||||
let mut pwd = shenv.vars().get_var("PWD").to_string();
|
||||
pwd.push('\n');
|
||||
|
||||
shenv.collect_redirs(redirs);
|
||||
shenv.ctx_mut().activate_rdrs()?;
|
||||
write_out(pwd)?;
|
||||
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn read_builtin(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs: _ } = rule {
|
||||
let argv = argv.drop_first();
|
||||
let mut argv_iter = argv.iter();
|
||||
// TODO: properly implement redirections
|
||||
// using activate_redirs() was causing issues, may require manual handling
|
||||
|
||||
let mut buf = vec![0u8; 1024];
|
||||
let bytes_read = read(0, &mut buf)?;
|
||||
buf.truncate(bytes_read);
|
||||
|
||||
let read_input = String::from_utf8_lossy(&buf).trim_end().to_string();
|
||||
|
||||
if let Some(var) = argv_iter.next() {
|
||||
/*
|
||||
let words: Vec<&str> = read_input.split_whitespace().collect();
|
||||
|
||||
for (var, value) in argv_iter.zip(words.iter().chain(std::iter::repeat(&""))) {
|
||||
shenv.vars_mut().set_var(&var.to_string(), value);
|
||||
}
|
||||
|
||||
// Assign the rest of the string to the first variable if there's only one
|
||||
if argv.len() == 1 {
|
||||
shenv.vars_mut().set_var(&first_var.to_string(), &read_input);
|
||||
}
|
||||
*/
|
||||
let var_name = shenv.input_slice(var.span()).to_string();
|
||||
shenv.vars_mut().set_var(&var_name, &read_input);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
log!(TRACE, "leaving read");
|
||||
shenv.set_code(0);
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn source(node: Node, shenv: &mut ShEnv) -> ShResult<()> {
|
||||
let rule = node.into_rule();
|
||||
if let NdRule::Command { argv, redirs } = rule {
|
||||
shenv.collect_redirs(redirs);
|
||||
let mut argv_iter = argv.into_iter().skip(1);
|
||||
while let Some(arg) = argv_iter.next() {
|
||||
let arg_raw = arg.as_raw(shenv);
|
||||
let arg_path = PathBuf::from(arg_raw);
|
||||
shenv.source_file(arg_path)?;
|
||||
}
|
||||
} else { unreachable!() }
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user