#[cfg(not(unix))]
compile_error!("YOM is available on UNIX-like systems only.");
use std::fs::File;
use std::io;
use std::io::{BufRead, BufReader};
mod builtins; mod externals; mod internals; use internals::helpers::*; use std::process::exit;
fn main() {
let mut args = std::env::args_os();
args.next();
let path = match args.next() {
Some(p) => p,
None => return,
};
let f = File::open(&path).expect("Expected file");
let mut input_stack: Vec<BufReader<File>> = Vec::new();
input_stack.push(BufReader::new(f));
let mut line = String::new();
let stdout = io::stdout();
let mut lock = stdout.lock();
let mut stderr = std::io::stderr();
let mut error_continue: bool = true;
let mut cd_hook: String = "".into();
let mut echo_hook: String = "".into();
let mut exit_hook: String = "".into();
let mut pwd_hook: String = "".into();
let mut read_hook: String = "".into();
while let Some(reader) = input_stack.last_mut() {
let bytes = reader.read_line(&mut line).unwrap();
if bytes == 0 {
input_stack.pop();
continue;
}
line = line.trim().into();
if line.starts_with("#") || line == "" {
line.clear();
continue;
} else if line == "then" {
line.clear();
continue;
} else if line == "fi" {
line.clear();
continue;
} else if line.starts_with(". ") || line.starts_with("source ") {
let mut parts = line.split_whitespace();
parts.next(); if let Some(path) = parts.next() {
let f = File::open(path).expect("Expected file");
input_stack.push(BufReader::new(f));
} else {
err_write("source requires a path", &mut stderr);
if !error_continue {
exit(1);
}
}
line.clear();
continue;
} else if line.starts_with("/") || line.starts_with("$") {
if line.ends_with(" &") {
externals::entry::background(line.strip_suffix(" &").unwrap()); line.clear();
continue;
} else if line.ends_with(" &") {
externals::entry::background(line.strip_suffix(" &").unwrap());
line.clear();
continue;
} else {
externals::entry::waitfor(line.as_str()); line.clear();
continue;
}
} else if line.starts_with("set ") {
let options = line.strip_prefix("set ").unwrap();
let mut flag = ' ';
if let Some(c) = options.chars().next() {
flag = c;
} else {
err_write("no flag included in set", &mut stderr);
if error_continue == false {
exit(1);
}
}
let options = options.strip_prefix("-").unwrap_or(options);
let options = options.strip_prefix("+").unwrap_or(options);
if options.contains("e") {
if flag == '+' {
error_continue = true;
} else if flag == '-' {
error_continue = false;
}
} else {
err_write("you missed (or mispelt) an option in set", &mut stderr);
if error_continue == false {
exit(1);
}
}
line.clear();
continue;
} else if line.starts_with('e') {
if line.starts_with("exec ") {
externals::entry::exec(line.as_str()); err_write("exec failed to overwrite process", &mut stderr);
if error_continue == false {
exit(1);
}
line.clear();
continue;
} else if line.starts_with("echo ") {
let str = line.strip_prefix("echo ").unwrap(); if str.starts_with('"') && str.ends_with('"') {
let str = str.strip_prefix('"').unwrap();
let str = str.strip_suffix('"').unwrap();
builtins::echo::echo(
str,
&mut lock,
&echo_hook,
&mut stderr,
error_continue,
); line.clear();
continue;
} else {
builtins::echo::echo(
str,
&mut lock,
&echo_hook,
&mut stderr,
error_continue,
); line.clear();
continue;
}
} else if line.starts_with("export ") {
let tmp = line.strip_prefix("export ").unwrap();
if let Some((name, data)) = tmp.split_once('=') {
unsafe {
let split = shell_words::split(data).expect("Invalid command syntax");
let data: String = split.join(" ");
std::env::set_var(name, data);
}
}
line.clear();
continue;
}
} else if line.starts_with("cd ") {
let dir = line.strip_prefix("cd ").unwrap(); if dir.starts_with('"') && dir.ends_with('"') {
let dir = dir.strip_prefix('"').unwrap(); let dir = dir.strip_suffix('"').unwrap(); let _ = builtins::cd::cd(dir, &cd_hook, &mut stderr, error_continue); line.clear();
continue;
} else {
let _ = builtins::cd::cd(dir, &cd_hook, &mut stderr, error_continue); line.clear();
continue;
}
} else if line == "pwd" {
let _ = builtins::pwd::pwd(&mut lock, &mut stderr, &pwd_hook); line.clear();
continue;
} else if line.starts_with("read") {
let prompt = line.strip_prefix("read ").unwrap(); if prompt.starts_with('"') && prompt.ends_with('"') {
let prompt = prompt.strip_prefix('"').unwrap(); let prompt = prompt.strip_suffix('"').unwrap(); let val = builtins::read::read(prompt, &read_hook); unsafe {
std::env::set_var(prompt, val);
}
line.clear();
continue;
} else {
let val = builtins::read::read(prompt, &read_hook); unsafe {
std::env::set_var(prompt, val);
}
line.clear();
continue;
}
} else if line == "exit" {
builtins::exit::exit(0, &exit_hook, &mut stderr, error_continue); line.clear();
continue;
} else if line.starts_with("exit ") {
let code = line.strip_prefix("exit ").unwrap();
let val = str_or_int(code, code);
if val == true {
let code: i32 = code.parse().unwrap_or(1); builtins::exit::exit(code, &exit_hook, &mut stderr, error_continue); line.clear();
continue;
} else {
err_write("exit requires an integer", &mut stderr);
if error_continue == false {
exit(1);
}
line.clear();
continue;
}
} else if line.starts_with("if [") {
let split = shell_words::split(&line).unwrap();
let left: &str = &split[2];
let operator: &str = &split[3];
let right: &str = &split[4];
let mut indent: i64 = 1;
if left.starts_with("$") || right.starts_with("$") {
let tmp = left.strip_prefix("$").unwrap_or(left);
let left = std::env::var_os(tmp).unwrap_or(tmp.into());
let left = left.to_str().expect("Not valid UTF-8");
let tmp = right.strip_prefix("$").unwrap_or(right);
let right = std::env::var_os(tmp).unwrap_or(tmp.into());
let right = right.to_str().expect("Not valid UTF-8");
let val = str_or_int(left, right);
if val == true {
let left: i64 = left.parse().unwrap();
let right: i64 = right.parse().unwrap();
let val = ncmp(left, right, operator);
if val == true {
line.clear();
continue;
} else {
line.clear();
while reader.read_line(&mut line).unwrap() > 0 {
let trimmed = line.trim();
if trimmed.starts_with("if [") {
indent += 1;
}
if trimmed == "fi" {
line.clear();
indent -= 1;
}
if indent == 0 {
line.clear();
break;
}
line.clear();
}
}
} else {
let val = strcmp(left, right, operator);
if val == true {
line.clear();
continue;
} else {
line.clear();
while reader.read_line(&mut line).unwrap() > 0 {
let trimmed = line.trim();
if trimmed.starts_with("if [") {
indent += 1;
}
if trimmed == "fi" {
line.clear();
indent -= 1;
}
if indent == 0 {
line.clear();
break;
}
line.clear();
}
}
continue;
}
} else {
let val = str_or_int(left, right);
if val == true {
let left: i64 = left.parse().unwrap();
let right: i64 = right.parse().unwrap();
let val = ncmp(left, right, operator);
if val == true {
line.clear();
continue;
} else {
line.clear();
while reader.read_line(&mut line).unwrap() > 0 {
let trimmed = line.trim();
if trimmed.starts_with("if [") {
indent += 1;
}
if trimmed == "fi" {
line.clear();
indent -= 1;
}
if indent == 0 {
line.clear();
break;
}
line.clear();
}
}
continue;
} else {
let val = strcmp(left, right, operator);
if val == true {
line.clear();
continue;
} else {
line.clear();
while reader.read_line(&mut line).unwrap() > 0 {
let trimmed = line.trim();
if trimmed.starts_with("if [") {
indent += 1;
}
if trimmed == "fi" {
line.clear();
indent -= 1;
}
if indent == 0 {
line.clear();
break;
}
line.clear();
}
}
}
}
line.clear();
continue;
} else if line.starts_with("hook ") {
let split = shell_words::split(&line).unwrap();
let injected: &str = &split[1];
let path = &split[2..];
let path: String = path.join("");
if injected == "cd" {
cd_hook = path
} else if injected == "echo" {
echo_hook = path;
} else if injected == "exit" {
exit_hook = path;
} else if injected == "pwd" {
pwd_hook = path;
} else if injected == "read" {
read_hook = path;
}
line.clear();
continue;
} else {
err_write("syntax error", &mut stderr);
if error_continue == false {
exit(1);
}
line.clear();
continue;
}
}
exit(0);
}