From 34c1b99256befce0205e8d95aa6ededd811cb7f8 Mon Sep 17 00:00:00 2001
From: Sag0Sag0 <Sag0Sag0@users.noreply.github.com>
Date: Sun, 18 Mar 2018 09:34:24 +1100
Subject: [PATCH] Add builtin type (#713)

* Add builtin type

* Add most of mmsticks changes

* made get_command_info() return a Cow<'a, str>
---
 src/lib/builtins/command_info.rs | 86 ++++++++++++++++++++++++++++++++
 src/lib/builtins/mod.rs          | 40 ++++-----------
 2 files changed, 97 insertions(+), 29 deletions(-)
 create mode 100644 src/lib/builtins/command_info.rs

diff --git a/src/lib/builtins/command_info.rs b/src/lib/builtins/command_info.rs
new file mode 100644
index 00000000..f18699cb
--- /dev/null
+++ b/src/lib/builtins/command_info.rs
@@ -0,0 +1,86 @@
+use sys;
+use shell::Shell;
+use shell::status::*;
+use builtins::man_pages::*;
+
+use std::env;
+use std::path::Path;
+use std::borrow::Cow;
+
+pub(crate) fn which(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
+    if check_help(args, MAN_WHICH) {
+        return Ok(SUCCESS)
+    }
+
+    if args.len() == 1 {
+        eprintln!("which: Expected at least 1 args, got only 0");
+        return Err(())
+    }
+
+    let mut result = SUCCESS;
+    for &command in &args[1..] {
+        if let Ok(c_type) = get_command_info(command, shell) {
+            match c_type.as_ref() {
+                "alias" => {
+                    let alias = shell.variables.aliases.get(command).unwrap();
+                    println!("{}: alias to {}", command, alias);
+                },
+                "function" => println!("{}: function", command),
+                "builtin" => println!("{}: built-in shell command", command),
+                _path => println!("{}", _path)
+            }
+        } else {
+            result = FAILURE;
+        }
+    }
+    Ok(result)
+}
+
+pub(crate) fn find_type(args: &[&str], shell: &mut Shell) -> Result<i32, ()> {
+    // Type does not accept help flags, aka "--help".
+    if args.len() == 1 {
+        eprintln!("type: Expected at least 1 args, got only 0");
+        return Err(())
+    }
+
+    let mut result = FAILURE;
+    for &command in &args[1..] {
+        if let Ok(c_type) = get_command_info(command, shell) {
+            match c_type.as_ref() {
+                "alias" => {
+                    let alias = shell.variables.aliases.get(command).unwrap();
+                    println!("{} is aliased to `{}`", command, alias);
+                },
+                // TODO Make it print the function.
+                "function" => println!("{} is a function", command), 
+                "builtin" => println!("{} is a shell builtin", command),
+                _path => println!("{} is {}", command, _path)
+            }
+            result = SUCCESS;
+        } else {
+            eprintln!("type: {}: not found", command);
+        }
+    }
+    Ok(result)
+}
+
+pub(crate) fn get_command_info<'a>(command: &str, shell: &mut Shell) -> Result<Cow<'a, str>, ()> {
+    if shell.variables.aliases.get(command).is_some() {
+        return Ok("alias".into())
+    } else if shell.functions.contains_key(command) {
+        return Ok("function".into())
+    } else if shell.builtins.contains_key(command) {
+        return Ok("builtin".into())
+    } else {
+        for path in env::var("PATH")
+            .unwrap_or("/bin".to_string())
+            .split(sys::PATH_SEPARATOR) 
+        {
+            let executable = Path::new(path).join(command);
+            if executable.is_file() {
+                return Ok(executable.display().to_string().into())
+            }
+        }
+    }
+    Err(())
+}
\ No newline at end of file
diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index c3cef783..eb39feef 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -4,6 +4,7 @@ pub mod functions;
 pub mod calc;
 pub mod random;
 
+mod command_info;
 mod conditionals;
 mod job_control;
 mod man_pages;
@@ -16,6 +17,7 @@ mod exec;
 mod ion;
 mod is;
 
+use self::command_info::*;
 use self::conditionals::{contains, ends_with, starts_with};
 use self::echo::echo;
 use self::exec::exec;
@@ -33,7 +35,6 @@ use types::Array;
 use std::env;
 use std::error::Error;
 use std::io::{self, Write};
-use std::path::Path;
 
 use parser::Terminator;
 use parser::pipelines::{PipeItem, Pipeline};
@@ -110,6 +111,7 @@ pub const BUILTINS: &'static BuiltinMap = &map!(
     "suspend" => builtin_suspend : "Suspends the shell with a SIGTSTOP signal",
     "test" => builtin_test : "Performs tests on files and text",
     "true" => builtin_true : "Do nothing, successfully",
+    "type" => builtin_type : "indicates how a command would be interpreted",
     "unalias" => builtin_unalias : "Delete an alias",
     "wait" => builtin_wait : "Waits until all running background processes have completed",
     "which" => builtin_which : "Shows the full path of commands"
@@ -606,37 +608,17 @@ fn builtin_exists(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_which(args: &[&str], shell: &mut Shell) -> i32 {
-    if check_help(args, MAN_WHICH) {
-        return SUCCESS;
+    match which(args, shell) {
+        Ok(result) => result,
+        Err(()) => FAILURE
     }
+}
 
-    let mut result = SUCCESS;
-    'outer: for &command in &args[1..] {
-        if let Some(alias) = shell.variables.aliases.get(command) {
-            println!("{}: alias to {}", command, alias);
-            continue;
-        } else if shell.functions.contains_key(command) {
-            println!("{}: function", command);
-            continue;
-        } else if shell.builtins.contains_key(command) {
-            println!("{}: built-in shell command", command);
-            continue;
-        } else {
-            for path in env::var("PATH")
-                .unwrap_or("/bin".to_string())
-                .split(sys::PATH_SEPARATOR)
-            {
-                let executable = Path::new(path).join(command);
-                if executable.is_file() {
-                    println!("{}", executable.display());
-                    continue 'outer;
-                }
-            }
-            result = FAILURE;
-            println!("{} not found", command);
-        }
+fn builtin_type(args: &[&str], shell: &mut Shell) -> i32 {
+    match find_type(args, shell) {
+        Ok(result) => result,
+        Err(()) => FAILURE
     }
-    result
 }
 
 fn builtin_isatty(args: &[&str], _: &mut Shell) -> i32 {
-- 
GitLab