use std::io::Read;
fn main() {
let args: Vec<String> = std::env::args().collect();
let mut shell = epsh::eval::Shell::new();
if args.len() > 1 {
let mut i = 1;
while i < args.len() {
if args[i] == "-c" {
if i + 1 >= args.len() {
eprintln!("epsh: -c requires an argument");
std::process::exit(2);
}
let script = &args[i + 1];
if i + 2 < args.len() {
shell.set_args(
&args[i + 2..]
.iter()
.map(|s| s.as_str())
.collect::<Vec<&str>>(),
);
}
let status = shell.run_script(script);
std::process::exit(status);
} else if args[i] == "-s" {
break;
} else if (args[i] == "-o" || args[i] == "+o") && i + 1 < args.len() {
let enable = args[i] == "-o";
i += 1;
match args[i].as_str() {
"pipefail" => shell.opts_mut().pipefail = enable,
"errexit" => shell.opts_mut().errexit = enable,
"nounset" => shell.opts_mut().nounset = enable,
"xtrace" => shell.opts_mut().xtrace = enable,
"noglob" => shell.opts_mut().noglob = enable,
"noexec" => shell.opts_mut().noexec = enable,
opt => {
eprintln!("epsh: unknown option: {opt}");
std::process::exit(2);
}
}
i += 1;
} else if args[i].starts_with('-') && args[i].len() > 1 {
for ch in args[i][1..].chars() {
match ch {
'e' => shell.opts_mut().errexit = true,
'u' => shell.opts_mut().nounset = true,
'x' => shell.opts_mut().xtrace = true,
'f' => shell.opts_mut().noglob = true,
'n' => shell.opts_mut().noexec = true,
_ => {
eprintln!("epsh: unknown option: -{ch}");
std::process::exit(2);
}
}
}
i += 1;
} else {
let filename = &args[i];
shell.set_args(&args[i..].iter().map(|s| s.as_str()).collect::<Vec<&str>>());
let content = match std::fs::read(filename) {
Ok(bytes) => epsh::encoding::bytes_to_str(&bytes),
Err(e) => {
eprintln!("epsh: {filename}: {e}");
std::process::exit(127);
}
};
let status = shell.run_script(&content);
std::process::exit(status);
}
}
}
if unsafe { libc::isatty(0) } != 0 {
eprintln!("epsh — embeddable POSIX shell");
eprintln!();
eprintln!(
"usage: epsh [-e] [-u] [-x] [-f] [-n] [-o option] [-c command] [script [args...]]"
);
eprintln!();
eprintln!(" -c command execute command string");
eprintln!(" -e exit on error (set -e)");
eprintln!(" -u error on unset variables (set -u)");
eprintln!(" -x print commands before execution (set -x)");
eprintln!(" -f disable pathname expansion / globbing (set -f)");
eprintln!(" -n parse but do not execute (syntax check) (set -n)");
eprintln!(" -o pipefail exit status is highest nonzero pipeline stage");
eprintln!(" -o errexit same as -e");
eprintln!(" -o nounset same as -u");
eprintln!(" -o xtrace same as -x");
eprintln!(" -o noglob same as -f");
eprintln!(" -o noexec same as -n");
eprintln!(" +o <option> disable option");
eprintln!(" script execute script file");
eprintln!(" (no args) read script from stdin (pipe)");
std::process::exit(0);
}
let mut bytes = Vec::new();
if let Err(e) = std::io::stdin().read_to_end(&mut bytes) {
eprintln!("epsh: {e}");
std::process::exit(1);
}
let input = epsh::encoding::bytes_to_str(&bytes);
let status = shell.run_script(&input);
std::process::exit(status);
}