#![feature(exclusive_range_pattern)]
extern crate docopt;
extern crate nix;
extern crate oursh;
extern crate termion;
extern crate dirs;
use std::{
env,
process::{Termination, ExitCode},
fs::File,
io::{self, Read},
cell::RefCell,
rc::Rc,
};
use nix::sys::wait::WaitStatus;
use docopt::{Docopt, Value};
use termion::is_tty;
use oursh::{
VERSION,
repl,
invocation::source_profile,
program::{parse_and_run, Runtime, Result, Error},
process::{Jobs, IO},
};
#[cfg(feature = "history")]
use oursh::repl::history::History;
const USAGE: &str = "
The oursh utility is a command language interpreter that shall execute commands
read from a command line string, the standard input, or a specified file.
Usage:
oursh [options] [<command_file> [<arguments>...]]
oursh -s [options] [<arguments>...]
oursh -c [options] <command_string> [<command_name> [<arguments>...]]
By default our will read commands from the command_file operand. If there are no
operands and the -c option is not specified, the -s option shall be assumed.
Options:
-c Read commands from the command_string operand.
-s Read commands from the standard input.
-i Specify that the shell is interactive.
--login Act as if invoked as a login shell.
-h --help Show this screen.
-v --verbose Print extra information.
-a --ast Print program ASTs.
-# --alternate Use alternate program syntax.
--posix Run using the (strict) POSIX language by default.
--init-file Override the default profile.
--rcfile and RC file locations for startup.
--noprofile Don't load and profile code on launch.
--norc
--debug
--debugger
--dump-po-strings
--dump-strings
--noediting
--restricted
--version
TODO: Read set [-abCefhmnuvx] [-o option] for a complete list of arguments.
";
fn main() -> MainResult {
let mut args = Docopt::new(USAGE)
.and_then(|d|
d.version(Some(VERSION.into()))
.argv(env::args().into_iter())
.parse())
.unwrap_or_else(|e| e.exit());
let mut jobs: Jobs = Rc::new(RefCell::new(vec![]));
let mut io = IO::default();
#[cfg(feature = "history")]
let mut history = History::load();
let mut runtime = Runtime {
io,
jobs: &mut jobs,
args: &args,
background: false,
#[cfg(feature = "history")]
history: &mut history,
};
if !args.get_bool("--noprofile") {
source_profile(&mut runtime);
}
if let Some(Value::Plain(Some(ref c))) = args.find("<command_string>") {
MainResult(parse_and_run(c, &mut runtime))
} else if let Some(Value::Plain(Some(ref filename))) = args.find("<command_file>") {
let mut file = File::open(filename)
.unwrap_or_else(|_| panic!("error opening file: {}", filename));
let mut text = String::new();
file.read_to_string(&mut text)
.expect("error reading file");
MainResult(parse_and_run(&text, &mut runtime))
} else {
let stdin = io::stdin();
if is_tty(&stdin) {
let stdout = io::stdout();
ctrlc::set_handler(move || println!()).unwrap();
let result = repl::start(stdin, stdout, &mut io, &mut jobs, &mut args);
MainResult(result)
} else {
let mut text = String::new();
stdin.lock().read_to_string(&mut text).unwrap();
MainResult(parse_and_run(&text, &mut runtime))
}
}
}
#[derive(Debug)]
struct MainResult(Result<WaitStatus>);
impl Termination for MainResult {
fn report(self) -> ExitCode {
match self.0 {
Ok(WaitStatus::Exited(_pid, code)) => ExitCode::from(code as u8),
Ok(WaitStatus::Signaled(_pid, _signal, _coredump)) => ExitCode::from(128),
Ok(_) => ExitCode::from(0), Err(Error::Read) => ExitCode::from(1),
Err(Error::Parse) => ExitCode::from(2),
Err(Error::Runtime) => ExitCode::from(127),
}
}
}