From 0d53220a95f6d2175254e97765124c64e2920310 Mon Sep 17 00:00:00 2001 From: Sag0Sag0 <Sag0Sag0@users.noreply.github.com> Date: Sun, 26 Nov 2017 10:03:50 +1100 Subject: [PATCH] Add builtin status (#603) The new status builtin accepts three arguments: - **-l**: returns true if the shell is a login shell. - **-i**: returns true if the shell is interactive. - **-f**: prints the filename of the currently running shell script or if there is none prints stdio. --- examples/basic_condition.ion | 0 manual/src/ch11-00-builtins.md | 16 ++++- setup.ion | 2 +- src/builtins/mod.rs | 17 +++++- src/builtins/status.rs | 107 +++++++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 5 deletions(-) mode change 100644 => 100755 examples/basic_condition.ion mode change 100755 => 100644 setup.ion create mode 100644 src/builtins/status.rs diff --git a/examples/basic_condition.ion b/examples/basic_condition.ion old mode 100644 new mode 100755 diff --git a/manual/src/ch11-00-builtins.md b/manual/src/ch11-00-builtins.md index 3322ac0b..e9dd023a 100644 --- a/manual/src/ch11-00-builtins.md +++ b/manual/src/ch11-00-builtins.md @@ -389,4 +389,18 @@ Waits until all running background processes have completed which COMMAND ``` -Shows the full path of commands \ No newline at end of file +Shows the full path of commands + +## status + +``` +status COMMAND +``` + +Evaluates the current runtime status + +### Options + +- **-l**: returns true if shell is a login shell +- **-i**: returns true if shell is interactive +- **-f**: prints the filename of the currently running script or stdio \ No newline at end of file diff --git a/setup.ion b/setup.ion old mode 100755 new mode 100644 index f3c265c0..cce3a906 --- a/setup.ion +++ b/setup.ion @@ -77,4 +77,4 @@ match "@args[1..3]" sudo rm ${SHAREDIR} -R case _ bad_arg "@args[1..3]" -end +end \ No newline at end of file diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 499e18bb..0c12399d 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -9,6 +9,7 @@ mod job_control; mod test; mod echo; mod set; +mod status; mod exists; mod ion; @@ -17,6 +18,7 @@ use self::echo::echo; use self::exists::exists; use self::functions::fn_; use self::ion::ion_docs; +use self::status::status; use self::source::source; use self::test::test; use self::variables::{alias, drop_alias, drop_array, drop_variable}; @@ -52,6 +54,7 @@ macro_rules! map { } }} +/// Builtins are in A-Z order. pub const BUILTINS: &'static BuiltinMap = &map!( "alias" => builtin_alias : "View, set or unset aliases", "and" => builtin_and : "Execute the command if the shell's previous status is success", @@ -125,9 +128,17 @@ impl BuiltinMap { } // Definitions of simple builtins go here - -// TODO: dummy function -fn builtin_status(_: &[&str], _: &mut Shell) -> i32 { SUCCESS } +fn builtin_status(args: &[&str], shell: &mut Shell) -> i32 { + match status(args, shell) { + Ok(()) => SUCCESS, + Err(why) => { + let stderr = io::stderr(); + let mut stderr = stderr.lock(); + let _ = stderr.write_all(why.as_bytes()); + FAILURE + } + } +} pub fn builtin_cd(args: &[&str], shell: &mut Shell) -> i32 { match shell.directory_stack.cd(args, &shell.variables) { diff --git a/src/builtins/status.rs b/src/builtins/status.rs new file mode 100644 index 00000000..21f83d1c --- /dev/null +++ b/src/builtins/status.rs @@ -0,0 +1,107 @@ +use std::env; +use std::error::Error; +use std::io::{Write, stdout}; + +use shell::Shell; + +bitflags! { + struct Flags : u8 { + const HELP = 1; + const LOGIN_SHELL = 2; + const INTERACTIVE = 4; + const FILENAME = 8; + } +} + +const MAN_PAGE: &'static str = r#"NAME + status - Evaluates the current runtime status + +SYNOPSIS + status [ -h | --help ] [-l] [-i] + +DESCRIPTION + With no arguments status displays the current login information of the shell. + +OPTIONS + -l + returns true if shell is a login shell. Also --is-login. + -i + returns true if shell is interactive. Also --is-interactive. + -f + prints the filename of the currently running script or stdio. Also --current-filename. +"#; // @MANEND + +pub(crate) fn status(args: &[&str], shell: &mut Shell) -> Result<(), String> { + let mut flags = Flags::empty(); + let shell_args: Vec<_> = env::args().collect(); + + let mut is_login = false; + if shell_args[0].chars().nth(0).unwrap() == '-' { + is_login = true; + } + + let args_len = args.len(); + if args_len == 1 { + if is_login { + println!("This is a login shell"); + } else { + println!("This is not a login shell"); + } + } else if args_len > 2 { + return Err("status takes one argument\n".to_string()); + } else { + for arg in args { + match *arg { + "--help" => flags |= Flags::HELP, + "--is-login" => flags |= Flags::LOGIN_SHELL, + "--is-interactive" => flags |= Flags::INTERACTIVE, + "--current-filename" => flags |= Flags::FILENAME, + _ => if arg.starts_with('-') { + match arg.chars().nth(1).unwrap() { + 'h' => flags |= Flags::HELP, + 'l' => flags |= Flags::LOGIN_SHELL, + 'i' => flags |= Flags::INTERACTIVE, + 'f' => flags |= Flags::FILENAME, + _ => () + } + } + } + } + let err = "".to_string(); + + if flags.contains(Flags::LOGIN_SHELL) && !is_login { + return Err(err); + } + + if flags.contains(Flags::INTERACTIVE) { + if shell.is_background_shell || shell.is_library { + return Err(err); + } + } + + if flags.contains(Flags::FILENAME) { + // TODO: This technique will not work if ion is renamed. + let sa_len = shell_args.len() - 1; + let last_sa = &shell_args[sa_len]; + let last_3: String = last_sa[last_sa.len() - 3 .. last_sa.len()].to_string(); + + if last_3 == "ion" { + println!("stdio"); + } else { + println!("{}", last_sa); + } + } + + let stdout = stdout(); + let mut stdout = stdout.lock(); + + if flags.contains(Flags::HELP) { + return match stdout.write_all(MAN_PAGE.as_bytes()).and_then(|_| stdout.flush()) { + Ok(_) => Ok(()), + Err(err) => Err(err.description().to_owned()), + } + } + } + Ok(()) +} + -- GitLab