From d096b4d1eebbe10f227412fed70ef7047e8f630d Mon Sep 17 00:00:00 2001
From: Sag0Sag0 <Sag0Sag0@users.noreply.github.com>
Date: Tue, 5 Dec 2017 12:14:10 +1100
Subject: [PATCH] Add manpages for almost all builtins. (#628)

* Start adding manpages

* Add manpages for all builtins

* Fix so that changes pass tests
---
 manual/src/ch07-01-conditionals.md |   8 +-
 src/builtins/echo.rs               |  47 +--
 src/builtins/exists.rs             | 143 ++-----
 src/builtins/is.rs                 |  28 +-
 src/builtins/job_control.rs        |  32 +-
 src/builtins/man_pages.rs          | 585 +++++++++++++++++++++++++++++
 src/builtins/mod.rs                | 141 ++++++-
 src/builtins/set.rs                |  33 +-
 src/builtins/status.rs             |  35 +-
 src/builtins/test.rs               | 183 ++-------
 10 files changed, 794 insertions(+), 441 deletions(-)
 create mode 100644 src/builtins/man_pages.rs

diff --git a/manual/src/ch07-01-conditionals.md b/manual/src/ch07-01-conditionals.md
index 8a2f608a..242c05f0 100644
--- a/manual/src/ch07-01-conditionals.md
+++ b/manual/src/ch07-01-conditionals.md
@@ -46,15 +46,15 @@ reason for a shell language to have multiple different keywords to end different
 
 ## Complete List of Conditional Builtins
 
-- [ ] and
+- [x] and
 - [ ] contains
-- [ ] exists
+- [x] exists
 - [ ] intersects
-- [ ] is
+- [x] is
 - [ ] isatty
 - [x] matches
 - [x] not
-- [ ] or
+- [x] or
 - [x] test
 - [ ] < (Polish Notation)
 - [ ] <= (Polish Notation)
diff --git a/src/builtins/echo.rs b/src/builtins/echo.rs
index 1ec9c7f2..45bacc25 100644
--- a/src/builtins/echo.rs
+++ b/src/builtins/echo.rs
@@ -2,52 +2,18 @@ use std::io::{self, BufWriter, Write};
 
 bitflags! {
     struct Flags : u8 {
-        const HELP = 1;
-        const ESCAPE = 2;
-        const NO_NEWLINE = 4;
-        const NO_SPACES = 8;
+        const ESCAPE = 1;
+        const NO_NEWLINE = 2;
+        const NO_SPACES = 4;
     }
 }
 
-
-const MAN_PAGE: &'static str = r#"NAME
-    echo - display a line of text
-
-SYNOPSIS
-    echo [ -h | --help ] [-e] [-n] [-s] [STRING]...
-
-DESCRIPTION
-    Print the STRING(s) to standard output.
-
-OPTIONS
-    -e
-        enable the interpretation of backslash escapes
-    -n
-        do not output the trailing newline
-    -s
-        do not separate arguments with spaces
-
-    Escape Sequences
-        When the -e argument is used, the following sequences will be interpreted:
-        \\  backslash
-        \a  alert (BEL)
-        \b  backspace (BS)
-        \c  produce no further output
-        \e  escape (ESC)
-        \f  form feed (FF)
-        \n  new line
-        \r  carriage return
-        \t  horizontal tab (HT)
-        \v  vertical tab (VT)
-"#; // @MANEND
-
 pub(crate) fn echo(args: &[&str]) -> Result<(), io::Error> {
     let mut flags = Flags::empty();
     let mut data: Vec<&str> = vec![];
 
     for arg in args {
         match *arg {
-            "--help" => flags |= Flags::HELP,
             "--escape" => flags |= Flags::ESCAPE,
             "--no-newline" => flags |= Flags::NO_NEWLINE,
             "--no-spaces" => flags |= Flags::NO_SPACES,
@@ -60,7 +26,6 @@ pub(crate) fn echo(args: &[&str]) -> Result<(), io::Error> {
                         'e' => short_flags |= Flags::ESCAPE,
                         'n' => short_flags |= Flags::NO_NEWLINE,
                         's' => short_flags |= Flags::NO_SPACES,
-                        'h' => short_flags |= Flags::HELP,
                         _ => {
                             is_opts = false;
                             break;
@@ -81,12 +46,6 @@ pub(crate) fn echo(args: &[&str]) -> Result<(), io::Error> {
     let stdout = io::stdout();
     let mut buffer = BufWriter::new(stdout.lock());
 
-    if flags.contains(Flags::HELP) {
-        buffer.write_all(MAN_PAGE.as_bytes())?;
-        buffer.flush()?;
-        return Ok(());
-    }
-
     let mut first = true;
     for arg in data[1..].iter().map(|x| x.as_bytes()) {
         if first {
diff --git a/src/builtins/exists.rs b/src/builtins/exists.rs
index 09d48f20..2240f639 100644
--- a/src/builtins/exists.rs
+++ b/src/builtins/exists.rs
@@ -2,92 +2,20 @@
 use smallstring::SmallString;
 #[cfg(test)]
 use smallvec::SmallVec;
-use std::error::Error;
 use std::fs;
-use std::io::{self, BufWriter};
 use std::os::unix::fs::PermissionsExt;
 
 use shell::Shell;
 #[cfg(test)]
 use shell::flow_control::{Function, Statement};
 
-const MAN_PAGE: &'static str = r#"NAME
-    exists - check whether items exist
-
-SYNOPSIS
-    exists [EXPRESSION]
-
-DESCRIPTION
-    Checks whether the given item exists and returns an exit status of 0 if it does, else 1.
-
-OPTIONS
-    -a ARRAY
-        array var is not empty
-
-    -b BINARY
-        binary is in PATH
-
-    -d PATH
-        path is a directory
-        This is the same as test -d
-
-    -f PATH
-        path is a file
-        This is the same as test -f
-
-    --fn FUNCTION
-        function is defined
-
-    -s STRING
-        string var is not empty
-
-    STRING
-        string is not empty
-        This is the same as test -n
-
-EXAMPLES
-    Test if the file exists:
-        exists -f FILE && echo "The FILE exists" || echo "The FILE does not exist"
-
-    Test if some-command exists in the path and is executable:
-        exists -b some-command && echo "some-command exists" || echo "some-command does not exist"
-
-    Test if variable exists AND is not empty
-        exists -s myVar && echo "myVar exists: $myVar" || echo "myVar does not exist or is empty"
-        NOTE: Don't use the '$' sigil, but only the name of the variable to check
-
-    Test if array exists and is not empty
-        exists -a myArr && echo "myArr exists: @myArr" || echo "myArr does not exist or is empty"
-        NOTE: Don't use the '@' sigil, but only the name of the array to check
-
-    Test if a function named 'myFunc' exists
-        exists --fn myFunc && myFunc || echo "No function with name myFunc found"
-
-AUTHOR
-    Written by Fabian Würfl.
-    Heavily based on implementation of the test builtin, which was written by Michael Murph.
-"#; // @MANEND
-
 pub(crate) fn exists(args: &[&str], shell: &Shell) -> Result<bool, String> {
-    let stdout = io::stdout();
-    let mut buffer = BufWriter::new(stdout.lock());
-
     let arguments = &args[1..];
-    evaluate_arguments(arguments, &mut buffer, shell)
+    evaluate_arguments(arguments, shell)
 }
 
-fn evaluate_arguments<W: io::Write>(
-    arguments: &[&str],
-    buffer: &mut W,
-    shell: &Shell,
-) -> Result<bool, String> {
+fn evaluate_arguments(arguments: &[&str], shell: &Shell) -> Result<bool, String> {
     match arguments.first() {
-        Some(&"--help") => {
-            // not handled by the second case, so that we don't have to pass the buffer around
-            buffer.write_all(MAN_PAGE.as_bytes()).map_err(|x| x.description().to_owned())?;
-            buffer.flush().map_err(|x| x.description().to_owned())?;
-            Ok(true)
-        }
         Some(&s) if s.starts_with("--") => {
             let (_, option) = s.split_at(2);
             // If no argument was given, return `SUCCESS`, as this means a string starting
@@ -215,47 +143,42 @@ fn function_is_defined(function: &str, shell: &Shell) -> bool {
 fn test_evaluate_arguments() {
     use parser::assignments::{KeyBuf, Primitive};
     let mut shell = Shell::new();
-    let mut sink = BufWriter::new(io::sink());
 
     // assert_eq!(evaluate_arguments(&[], &mut sink, &shell), Ok(false));
     // no parameters
-    assert_eq!(evaluate_arguments(&[], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&[], &shell), Ok(false));
     // multiple arguments
     // ignores all but the first argument
-    assert_eq!(evaluate_arguments(&["foo", "bar"], &mut sink, &shell), Ok(true));
-
-    // check whether --help returns SUCCESS
-    assert_eq!(evaluate_arguments(&["--help"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["--help", "unused", "params"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["foo", "bar"], &shell), Ok(true));
 
     // check `exists STRING`
-    assert_eq!(evaluate_arguments(&[""], &mut sink, &shell), Ok(false));
-    assert_eq!(evaluate_arguments(&["string"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["string with space"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-startswithdash"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&[""], &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["string"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["string with space"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-startswithdash"], &shell), Ok(true));
 
     // check `exists -a`
     // no argument means we treat it as a string
-    assert_eq!(evaluate_arguments(&["-a"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-a"], &shell), Ok(true));
     shell.variables.set_array("emptyarray", SmallVec::from_vec(Vec::new()));
-    assert_eq!(evaluate_arguments(&["-a", "emptyarray"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-a", "emptyarray"], &shell), Ok(false));
     let mut vec = Vec::new();
     vec.push("element".to_owned());
     shell.variables.set_array("array", SmallVec::from_vec(vec));
-    assert_eq!(evaluate_arguments(&["-a", "array"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-a", "array"], &shell), Ok(true));
     shell.variables.unset_array("array");
-    assert_eq!(evaluate_arguments(&["-a", "array"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-a", "array"], &shell), Ok(false));
 
     // check `exists -b`
     // TODO: see test_binary_is_in_path()
     // no argument means we treat it as a string
-    assert_eq!(evaluate_arguments(&["-b"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-b"], &shell), Ok(true));
     let oldpath = shell.get_var("PATH").unwrap_or("/usr/bin".to_owned());
     shell.set_var("PATH", "testing/");
 
-    assert_eq!(evaluate_arguments(&["-b", "executable_file"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-b", "empty_file"], &mut sink, &shell), Ok(false));
-    assert_eq!(evaluate_arguments(&["-b", "file_does_not_exist"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-b", "executable_file"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-b", "empty_file"], &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-b", "file_does_not_exist"], &shell), Ok(false));
 
     // restore original PATH. Not necessary for the currently defined test cases but this might
     // change in the future? Better safe than sorry!
@@ -263,33 +186,33 @@ fn test_evaluate_arguments() {
 
     // check `exists -d`
     // no argument means we treat it as a string
-    assert_eq!(evaluate_arguments(&["-d"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-d", "testing/"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-d", "testing/empty_file"], &mut sink, &shell), Ok(false));
-    assert_eq!(evaluate_arguments(&["-d", "does/not/exist/"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-d"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-d", "testing/"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-d", "testing/empty_file"], &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-d", "does/not/exist/"], &shell), Ok(false));
 
     // check `exists -f`
     // no argument means we treat it as a string
-    assert_eq!(evaluate_arguments(&["-f"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-f", "testing/"], &mut sink, &shell), Ok(false));
-    assert_eq!(evaluate_arguments(&["-f", "testing/empty_file"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-f", "does-not-exist"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-f"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-f", "testing/"], &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-f", "testing/empty_file"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-f", "does-not-exist"], &shell), Ok(false));
 
     // check `exists -s`
     // no argument means we treat it as a string
-    assert_eq!(evaluate_arguments(&["-s"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-s"], &shell), Ok(true));
     shell.set_var("emptyvar", "");
-    assert_eq!(evaluate_arguments(&["-s", "emptyvar"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-s", "emptyvar"], &shell), Ok(false));
     shell.set_var("testvar", "foobar");
-    assert_eq!(evaluate_arguments(&["-s", "testvar"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-s", "testvar"], &shell), Ok(true));
     shell.variables.unset_var("testvar");
-    assert_eq!(evaluate_arguments(&["-s", "testvar"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-s", "testvar"], &shell), Ok(false));
     // also check that it doesn't trigger on arrays
     let mut vec = Vec::new();
     vec.push("element".to_owned());
     shell.variables.unset_var("array");
     shell.variables.set_array("array", SmallVec::from_vec(vec));
-    assert_eq!(evaluate_arguments(&["-s", "array"], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["-s", "array"], &shell), Ok(false));
 
     // check `exists --fn`
     let name_str = "test_function";
@@ -302,13 +225,13 @@ fn test_evaluate_arguments() {
 
     shell.functions.insert(name.clone(), Function::new(Some(description), name, args, statements));
 
-    assert_eq!(evaluate_arguments(&["--fn", name_str], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["--fn", name_str], &shell), Ok(true));
     shell.functions.remove(name_str);
-    assert_eq!(evaluate_arguments(&["--fn", name_str], &mut sink, &shell), Ok(false));
+    assert_eq!(evaluate_arguments(&["--fn", name_str], &shell), Ok(false));
 
     // check invalid flags / parameters (should all be treated as strings and therefore succeed)
-    assert_eq!(evaluate_arguments(&["--foo"], &mut sink, &shell), Ok(true));
-    assert_eq!(evaluate_arguments(&["-x"], &mut sink, &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["--foo"], &shell), Ok(true));
+    assert_eq!(evaluate_arguments(&["-x"], &shell), Ok(true));
 }
 
 #[test]
diff --git a/src/builtins/is.rs b/src/builtins/is.rs
index 39cda14c..79b80a4b 100644
--- a/src/builtins/is.rs
+++ b/src/builtins/is.rs
@@ -1,22 +1,6 @@
-use std::error::Error;
-use std::io::{stdout, Write};
-
+use builtins::man_pages::{check_help, MAN_IS};
 use shell::Shell;
 
-const MAN_PAGE: &'static str = r#"NAME
-    is - Checks if two arguments are the same
-
-SYNOPSIS
-    is [ -h | --help ] [not]
-
-DESCRIPTION
-    Returns 0 if the two arguments are equal
-
-OPTIONS
-    not
-        returns 0 if the two arguments are not equal.
-"#; // @MANEND
-
 pub(crate) fn is(args: &[&str], shell: &mut Shell) -> Result<(), String> {
     match args.len() {
         4 => if args[1] != "not" {
@@ -27,15 +11,7 @@ pub(crate) fn is(args: &[&str], shell: &mut Shell) -> Result<(), String> {
         3 => if eval_arg(args[1], shell) != eval_arg(args[2], shell) {
             return Err("".to_string());
         },
-        2 => if args[1] == "-h" || args[1] == "--help" {
-            let stdout = stdout();
-            let mut stdout = stdout.lock();
-
-            return match stdout.write_all(MAN_PAGE.as_bytes()).and_then(|_| stdout.flush()) {
-                Ok(_) => Ok(()),
-                Err(err) => Err(err.description().to_owned()),
-            };
-        } else {
+        2 => if !check_help(args, MAN_IS) {
             return Err("is needs 3 or 4 arguments\n".to_string());
         },
         _ => return Err("is needs 3 or 4 arguments\n".to_string()),
diff --git a/src/builtins/job_control.rs b/src/builtins/job_control.rs
index cdf4076e..f50e1a00 100644
--- a/src/builtins/job_control.rs
+++ b/src/builtins/job_control.rs
@@ -4,37 +4,12 @@ use shell::Shell;
 use shell::job_control::{JobControl, ProcessState};
 use shell::signals;
 use shell::status::*;
-use std::error::Error;
-use std::io::{stderr, stdout, Write};
-
-const DISOWN_MAN_PAGE: &'static str = r#"NAME
-    disown - Disown processes
-
-SYNOPSIS
-    disown [ --help | -r | -h | -a ][PID...]
-
-DESCRIPTION
-    Disowning a process removes that process from the shell's background process table.
-
-OPTIONS
-    -r  Remove all running jobs from the background process list.
-    -h  Specifies that each job supplied will not receive the SIGHUP signal when the shell receives a SIGHUP.
-    -a  If no job IDs were supplied, remove all jobs from the background process list.
-"#;
+use std::io::{stderr, Write};
 
 /// Disowns given process job IDs, and optionally marks jobs to not receive SIGHUP signals.
 /// The `-a` flag selects all jobs, `-r` selects all running jobs, and `-h` specifies to mark
 /// SIGHUP ignoral.
 pub(crate) fn disown(shell: &mut Shell, args: &[&str]) -> Result<(), String> {
-    fn print_help(ret: Result<(), String>) -> Result<(), String> {
-        let stdout = stdout();
-        let mut stdout = stdout.lock();
-
-        return match stdout.write_all(DISOWN_MAN_PAGE.as_bytes()).and_then(|_| stdout.flush()) {
-            Ok(_) => ret,
-            Err(err) => Err(err.description().to_owned()),
-        };
-    }
 
     const NO_SIGHUP: u8 = 1;
     const ALL_JOBS: u8 = 2;
@@ -47,9 +22,6 @@ pub(crate) fn disown(shell: &mut Shell, args: &[&str]) -> Result<(), String> {
             "-a" => flags |= ALL_JOBS,
             "-h" => flags |= NO_SIGHUP,
             "-r" => flags |= RUN_JOBS,
-            "--help" => {
-                return print_help(Ok(()));
-            }
             _ => match arg.parse::<u32>() {
                 Ok(jobspec) => jobspecs.push(jobspec),
                 Err(_) => {
@@ -60,7 +32,7 @@ pub(crate) fn disown(shell: &mut Shell, args: &[&str]) -> Result<(), String> {
     }
 
     if flags == 0 {
-        return print_help(Err("must provide arguments".to_owned()));
+        return Err("must provide arguments".to_owned());
     } else if (flags & ALL_JOBS) == 0 && jobspecs.is_empty() {
         return Err("must provide a jobspec with -h or -r".to_owned());
     }
diff --git a/src/builtins/man_pages.rs b/src/builtins/man_pages.rs
new file mode 100644
index 00000000..57eb541f
--- /dev/null
+++ b/src/builtins/man_pages.rs
@@ -0,0 +1,585 @@
+use std::error::Error;
+use std::io::{stdout, Write};
+
+pub(crate) fn print_man(man_page: &'static str) {
+    let stdout = stdout();
+    let mut stdout = stdout.lock();
+    match stdout.write_all(man_page.as_bytes()).and_then(|_| stdout.flush()) {
+        Ok(_) => (),
+        Err(err) => panic!("{}", err.description().to_owned()),
+    }
+}
+
+pub(crate) fn check_help(args: &[&str], man_page: &'static str) -> bool {
+    for arg in args {
+        if *arg == "-h" || *arg == "--help" {
+            print_man(man_page);
+            return true
+        }
+    }
+    false
+}
+
+pub(crate) const MAN_STATUS: &'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 the shell is a login shell. Also --is-login.
+    -i
+        returns true if the shell is interactive. Also --is-interactive.
+    -f
+        prints the filename of the currently running script or else stdio. Also --current-filename.
+"#;
+
+pub(crate) const MAN_CD: &'static str = r#"NAME
+    cd - Change directory.
+
+SYNOPSIS
+    cd DIRECTORY
+
+DESCRIPTION
+    Without arguments cd changes the working directory to your home directory.
+
+    With arguments cd changes the working directory to the directory you provided.
+
+"#;
+
+pub(crate) const MAN_BOOL: &'static str = r#"NAME
+    bool - Returns true if the value given to it is equal to '1' or 'true'.
+
+SYNOPSIS
+    bool VALUE
+
+DESCRIPTION
+    Returns true if the value given to it is equal to '1' or 'true'.
+"#;
+
+pub(crate) const MAN_IS: &'static str = r#"NAME
+    is - Checks if two arguments are the same
+
+SYNOPSIS
+    is [ -h | --help ] [not]
+
+DESCRIPTION
+    Returns 0 if the two arguments are equal
+
+OPTIONS
+    not
+        returns 0 if the two arguments are not equal.
+"#;
+
+pub(crate) const MAN_DIRS: &'static str = r#"NAME
+    dirs - prints the directory stack
+
+SYNOPSIS
+    dirs
+
+DESCRIPTION
+    dirs prints the current directory stack.
+"#;
+
+pub(crate) const MAN_PUSHD: &'static str = r#"NAME
+    pushd - push a directory to the directory stack
+
+SYNOPSIS
+    pushd DIRECTORY
+
+DESCRIPTION
+    pushd pushes a directory to the directory stack.
+"#;
+
+pub(crate) const MAN_POPD: &'static str = r#"NAME
+    popd - shift through the directory stack
+
+SYNOPSIS
+    popd
+
+DESCRIPTION
+    popd removes the top directory from the directory stack and changes the working directory to the new top directory. 
+    pushd adds directories to the stack.
+"#;
+
+/*pub(crate) const MAN_FN: &'static str = r#"NAME
+    fn - print a list of all functions or create a function
+
+SYNOPSIS
+    fn
+
+    fn example arg:int
+        echo $arg
+    end
+
+DESCRIPTION
+    fn prints a list of all functions that exist in the shell or creates a function when combined
+    with the 'end' keyword. Functions can have type hints, to tell ion to check the type of a 
+    functions arguments. An error will occur if an argument supplied to a function is of the wrong type.
+    The supported types in ion are, [], bool, bool[], float, float[], int, int[], str, str[].
+
+    Functions are called by typing the function name and then the function arguments, separated
+    by a space.
+
+    fn example arg0:int arg1:int
+        echo $arg
+    end
+
+    example 1
+"#;*/
+
+pub(crate) const MAN_READ: &'static str = r#"NAME
+    read - read a line of input into some variables
+
+SYNOPSIS
+    read VARIABLES...
+
+DESCRIPTION
+    For each variable reads from standard input and stores the results in the variable.
+"#;
+
+pub(crate) const MAN_DROP: &'static str = r#"NAME
+    drop - delete some variables or arrays
+
+SYNOPSIS
+    drop [ -a ] VARIABLES...
+
+DESCRIPTION
+    Deletes the variables given to it as arguments. The variables name must be supplied.
+    Instead of '$x' use 'x'.
+
+OPTIONS
+    -a
+        Instead of deleting variables deletes arrays.
+"#;
+
+pub(crate) const MAN_SET: &'static str = r#"NAME
+    set - Set or unset values of shell options and positional parameters.
+
+SYNOPSIS
+    set [ --help ] [-e | +e] [-x | +x] [-o [vi | emacs]] [- | --] [STRING]...
+
+DESCRIPTION
+    Shell options may be set using the '-' character, and unset using the '+' character.
+
+OPTIONS
+    -e  Exit immediately if a command exits with a non-zero status.
+
+    -o  Specifies that an argument will follow that sets the key map.
+        The keymap argument may be either `vi` or `emacs`.
+
+    -x  Specifies that commands will be printed as they are executed.
+
+    --  Following arguments will be set as positional arguments in the shell.
+        If no argument are supplied, arguments will be unset.
+
+    -   Following arguments will be set as positional arguments in the shell.
+        If no arguments are suppled, arguments will not be unset.
+"#;
+
+pub(crate) const MAN_EVAL: &'static str = r#"NAME
+    eval - evaluates the specified commands
+
+SYNOPSIS
+    eval COMMANDS...
+
+DESCRIPTION
+    eval evaluates the given arguments as a command. If more than one argument is given,
+    all arguments are joined using a space as a separator.
+"#;
+
+pub(crate) const MAN_HISTORY: &'static str = r#"NAME
+    history - print command history
+
+SYNOPSIS
+    history
+
+DESCRIPTION
+    Prints the command history.
+"#;
+
+pub(crate) const MAN_SOURCE: &'static str = r#"NAME
+    source - evaluates given file
+
+SYNOPSIS
+    source FILEPATH
+
+DESCRIPTION
+    Evaluates the commands in a specified file in the current shell. All changes in shell 
+    variables will affect the current shell because of this.
+"#;
+
+pub(crate) const MAN_ECHO: &'static str = r#"NAME
+    echo - display a line of text
+
+SYNOPSIS
+    echo [ -h | --help ] [-e] [-n] [-s] [STRING]...
+
+DESCRIPTION
+    Print the STRING(s) to standard output.
+
+OPTIONS
+    -e
+        enable the interpretation of backslash escapes
+    -n
+        do not output the trailing newline
+    -s
+        do not separate arguments with spaces
+
+    Escape Sequences
+        When the -e argument is used, the following sequences will be interpreted:
+        \\  backslash
+        \a  alert (BEL)
+        \b  backspace (BS)
+        \c  produce no further output
+        \e  escape (ESC)
+        \f  form feed (FF)
+        \n  new line
+        \r  carriage return
+        \t  horizontal tab (HT)
+        \v  vertical tab (VT)
+"#;
+
+pub(crate) const MAN_TEST: &'static str = r#"NAME
+    test - perform tests on files and text
+
+SYNOPSIS
+    test [EXPRESSION]
+
+DESCRIPTION
+    Tests the expressions given and returns an exit status of 0 if true, else 1.
+
+OPTIONS
+    -n STRING
+        the length of STRING is nonzero
+
+    STRING
+        equivalent to -n STRING
+
+    -z STRING
+        the length of STRING is zero
+
+    STRING = STRING
+        the strings are equivalent
+
+    STRING != STRING
+        the strings are not equal
+
+    INTEGER -eq INTEGER
+        the integers are equal
+
+    INTEGER -ge INTEGER
+        the first INTEGER is greater than or equal to the first INTEGER
+
+    INTEGER -gt INTEGER
+        the first INTEGER is greater than the first INTEGER
+
+    INTEGER -le INTEGER
+        the first INTEGER is less than or equal to the first INTEGER
+
+    INTEGER -lt INTEGER
+        the first INTEGER is less than the first INTEGER
+
+    INTEGER -ne INTEGER
+        the first INTEGER is not equal to the first INTEGER
+
+    FILE -ef FILE
+        both files have the same device and inode numbers
+
+    FILE -nt FILE
+        the first FILE is newer than the second FILE
+
+    FILE -ot FILE
+        the first file is older than the second FILE
+
+    -b FILE
+        FILE exists and is a block device
+
+    -c FILE
+        FILE exists and is a character device
+
+    -d FILE
+        FILE exists and is a directory
+
+    -e FILE
+        FILE exists
+
+    -f FILE
+        FILE exists and is a regular file
+
+    -h FILE
+        FILE exists and is a symbolic link (same as -L)
+
+    -L FILE
+        FILE exists and is a symbolic link (same as -h)
+
+    -r FILE
+        FILE exists and read permission is granted
+
+    -s FILE
+        FILE exists and has a file size greater than zero
+
+    -S FILE
+        FILE exists and is a socket
+
+    -w FILE
+        FILE exists and write permission is granted
+
+    -x FILE
+        FILE exists and execute (or search) permission is granted
+
+EXAMPLES
+    Test if the file exists:
+        test -e FILE && echo "The FILE exists" || echo "The FILE does not exist"
+
+    Test if the file exists and is a regular file, and if so, write to it:
+        test -f FILE && echo "Hello, FILE" >> FILE || echo "Cannot write to a directory"
+
+    Test if 10 is greater than 5:
+        test 10 -gt 5 && echo "10 is greater than 5" || echo "10 is not greater than 5"
+
+    Test if the user is running a 64-bit OS (POSIX environment only):
+        test $(getconf LONG_BIT) = 64 && echo "64-bit OS" || echo "32-bit OS"
+
+AUTHOR
+    Written by Michael Murphy.
+"#;
+
+pub(crate) const MAN_RANDOM: &'static str = r#"NAME
+    random - generate a random number
+
+SYNOPSIS
+    random
+    random START END
+
+DESCRIPTION
+    random generates a pseudo-random integer. IT IS NOT SECURE.
+    The range depends on what arguments you pass. If no arguments are given the range is [0, 32767]. 
+    If two arguments are given the range is [START, END].
+"#;
+
+pub(crate) const MAN_TRUE: &'static str = r#"NAME
+    true - does nothing successfully
+
+SYNOPSIS
+    true
+
+DESCRIPTION
+    Sets the exit status to 0.
+"#;
+
+pub(crate) const MAN_FALSE: &'static str = r#"NAME
+    false - does nothing unsuccessfully
+
+SYNOPSIS
+    false
+
+DESCRIPTION
+    Sets the exit status to 1.
+"#;
+
+pub(crate) const MAN_JOBS: &'static str = r#"NAME
+    jobs - list all jobs running in the background
+
+SYNOPSIS
+    jobs
+
+DESCRIPTION
+    Prints a list of all jobs running in the background.
+"#;
+
+pub(crate) const MAN_BG: &'static str = r#"NAME
+    bg - sends jobs to background
+
+SYNOPSIS
+    bg PID
+
+DESCRIPTION
+    bg sends the job to the background resuming it if it has stopped.
+"#;
+
+pub(crate) const MAN_FG: &'static str = r#"NAME
+    fg - bring job to foreground
+
+SYNOPSIS
+    fg PID
+
+DESCRIPTION
+    fg brings the specified job to foreground resuming it if it has stopped.
+"#;
+
+pub(crate) const MAN_SUSPEND: &'static str = r#"NAME
+    suspend - suspend the current shell
+
+SYNOPSIS
+    suspend
+
+DESCRIPTION
+    Suspends the current shell by sending it the SIGTSTP signal, 
+    returning to the parent process. It can be resumed by sending it SIGCONT.
+"#;
+
+pub(crate) const MAN_DISOWN: &'static str = r#"NAME
+    disown - Disown processes
+
+SYNOPSIS
+    disown [ --help | -r | -h | -a ][PID...]
+
+DESCRIPTION
+    Disowning a process removes that process from the shell's background process table.
+
+OPTIONS
+    -r  Remove all running jobs from the background process list.
+    -h  Specifies that each job supplied will not receive the SIGHUP signal when the shell receives a SIGHUP.
+    -a  If no job IDs were supplied, remove all jobs from the background process list.
+"#;
+
+pub(crate) const MAN_EXIT: &'static str = r#"NAME
+    exit - exit the shell
+
+SYNOPSIS
+    exit
+
+DESCRIPTION
+    Makes ion exit. The exit status will be that of the last command executed.
+"#;
+
+pub(crate) const MAN_MATCHES: &'static str = r#"NAME
+    matches - checks if the second argument contains any portion of the first.
+
+SYNOPSIS
+    matches VALUE VALUE
+
+DESCRIPTION
+    Makes the exit status equal 0 if the first argument contains the second.
+    Otherwise matches makes the exit status equal 1.
+
+EXAMPLES
+    Returns true:
+        matches xs x
+    Returns false:
+        matches x xs
+"#;
+
+pub(crate) const MAN_NOT: &'static str = r#"NAME
+    not - reverses the exit status of a job
+
+SYNOPSIS
+    not
+
+DESCRIPTION
+    not reverses the exit status of a job. If the exit status is 1 not returns 0 and vice versa.
+"#;
+
+pub(crate) const MAN_AND: &'static str = r#"NAME
+    and - check if to execute another command and a boolean gate.
+
+SYNOPSIS
+    COMMAND and COMMAND
+    COMMAND; and COMMAND
+
+DESCRIPTION
+    and changes the exit status to 0 if the previous command and the next command are true. 
+    and can also be used to execute multiple commands if they all are successful. '&&' is preferred to 'and'
+    in if, while and all other similar conditional statements.  
+
+EXAMPLES
+    Returns an exit of status 0:
+        true and true
+    Returns an exit of status 1:
+        true and false
+
+    Executes all the commands:
+        echo "1"; and echo "2"
+    Executes no commands:
+        false; and echo "1"
+"#;
+
+pub(crate) const MAN_OR: &'static str = r#"NAME
+    or - conditionally run a command
+
+SYNOPSIS
+    COMMAND; or COMMAND
+
+DESCRIPTION
+    or can be used to execute a command if the exit status of the previous command is not 0.
+    or can also be used in if, while and other similar statements, however '||' is preferred.
+
+EXAMPLE
+    Executes all of the code block, prints 2:
+        false; or echo "2" 
+    Does not execute all of the code block, prints 1:
+        echo "1"; or echo "2"
+"#;
+
+pub(crate) const MAN_EXISTS: &'static str = r#"NAME
+    exists - check whether items exist
+
+SYNOPSIS
+    exists [EXPRESSION]
+
+DESCRIPTION
+    Checks whether the given item exists and returns an exit status of 0 if it does, else 1.
+
+OPTIONS
+    -a ARRAY
+        array var is not empty
+
+    -b BINARY
+        binary is in PATH
+
+    -d PATH
+        path is a directory
+        This is the same as test -d
+
+    -f PATH
+        path is a file
+        This is the same as test -f
+
+    --fn FUNCTION
+        function is defined
+
+    -s STRING
+        string var is not empty
+
+    STRING
+        string is not empty
+        This is the same as test -n
+
+EXAMPLES
+    Test if the file exists:
+        exists -f FILE && echo "The FILE exists" || echo "The FILE does not exist"
+
+    Test if some-command exists in the path and is executable:
+        exists -b some-command && echo "some-command exists" || echo "some-command does not exist"
+
+    Test if variable exists AND is not empty
+        exists -s myVar && echo "myVar exists: $myVar" || echo "myVar does not exist or is empty"
+        NOTE: Don't use the '$' sigil, but only the name of the variable to check
+
+    Test if array exists and is not empty
+        exists -a myArr && echo "myArr exists: @myArr" || echo "myArr does not exist or is empty"
+        NOTE: Don't use the '@' sigil, but only the name of the array to check
+
+    Test if a function named 'myFunc' exists
+        exists --fn myFunc && myFunc || echo "No function with name myFunc found"
+
+AUTHOR
+    Written by Fabian Würfl.
+    Heavily based on implementation of the test builtin, which was written by Michael Murph.
+"#;
+
+pub(crate) const MAN_WHICH: &'static str = r#"NAME
+    which - locate a program file in the current user's path
+
+SYNOPSIS
+    which PROGRAM
+
+DESCRIPTION
+    The which utility takes a command name and searches the path for the executable file that would 
+    be run had the command been executed.
+"#;
\ No newline at end of file
diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs
index 32e74ae5..72ecc24c 100644
--- a/src/builtins/mod.rs
+++ b/src/builtins/mod.rs
@@ -6,6 +6,7 @@ pub mod random;
 
 mod conditionals;
 mod job_control;
+mod man_pages;
 mod test;
 mod echo;
 mod set;
@@ -20,6 +21,7 @@ use self::exists::exists;
 use self::functions::fn_;
 use self::ion::ion_docs;
 use self::is::is;
+use self::man_pages::*;
 use self::source::source;
 use self::status::status;
 use self::test::test;
@@ -58,6 +60,12 @@ macro_rules! map {
     }
 }}
 
+/// If you are implementing a builtin add it to the table below, create a well named manpage in man_pages 
+/// and check for help flags by adding to the start of your builtin the following
+/// if check_help(args, MAN_CD) {
+///     return SUCCESS
+/// }
+
 /// Builtins are in A-Z order.
 pub const BUILTINS: &'static BuiltinMap = &map!(
     "alias" => builtin_alias : "View, set or unset aliases",
@@ -147,6 +155,10 @@ fn builtin_status(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 pub fn builtin_cd(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_CD) {
+        return SUCCESS
+    }
+
     match shell.directory_stack.cd(args, &shell.variables) {
         Ok(()) => SUCCESS,
         Err(why) => {
@@ -172,16 +184,14 @@ fn builtin_bool(args: &[&str], shell: &mut Shell) -> i32 {
         None => "",
     };
 
-    let help_msg = "DESCRIPTION: If the value is '1' or 'true', bool returns the 0 exit \
-                    status\nusage: bool <value>";
     match sh_var {
         "1" => (),
         "true" => (),
         _ => match args[1] {
             "1" => (),
             "true" => (),
-            "--help" => println!("{}", help_msg),
-            "-h" => println!("{}", help_msg),
+            "--help" => print_man(MAN_BOOL),
+            "-h" => print_man(MAN_BOOL),
             _ => return FAILURE,
         },
     }
@@ -200,9 +210,18 @@ fn builtin_is(args: &[&str], shell: &mut Shell) -> i32 {
     }
 }
 
-fn builtin_dirs(args: &[&str], shell: &mut Shell) -> i32 { shell.directory_stack.dirs(args) }
+fn builtin_dirs(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_DIRS) {
+        return SUCCESS
+    }
+
+    shell.directory_stack.dirs(args) 
+}
 
 fn builtin_pushd(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_PUSHD) {
+        return SUCCESS
+    }
     match shell.directory_stack.pushd(args, &shell.variables) {
         Ok(()) => SUCCESS,
         Err(why) => {
@@ -215,6 +234,10 @@ fn builtin_pushd(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_popd(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_POPD) {
+        return SUCCESS
+    }
+
     match shell.directory_stack.popd(args) {
         Ok(()) => SUCCESS,
         Err(why) => {
@@ -235,11 +258,22 @@ fn builtin_unalias(args: &[&str], shell: &mut Shell) -> i32 {
     drop_alias(&mut shell.variables, args)
 }
 
-fn builtin_fn(_: &[&str], shell: &mut Shell) -> i32 { fn_(&mut shell.functions) }
+// TODO There is a man page for fn however the -h and --help flags are not checked for.
+fn builtin_fn(_: &[&str], shell: &mut Shell) -> i32 {
+    fn_(&mut shell.functions)
+}
 
-fn builtin_read(args: &[&str], shell: &mut Shell) -> i32 { shell.variables.read(args) }
+fn builtin_read(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_READ) {
+        return SUCCESS
+    }
+    shell.variables.read(args) 
+}
 
 fn builtin_drop(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_DROP) {
+        return SUCCESS
+    }
     if args.len() >= 2 && args[1] == "-a" {
         drop_array(&mut shell.variables, args)
     } else {
@@ -247,9 +281,17 @@ fn builtin_drop(args: &[&str], shell: &mut Shell) -> i32 {
     }
 }
 
-fn builtin_set(args: &[&str], shell: &mut Shell) -> i32 { set::set(args, shell) }
+fn builtin_set(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_SET) {
+        return SUCCESS
+    }
+    set::set(args, shell)
+}
 
 fn builtin_eval(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_EVAL) {
+        return SUCCESS
+    }
     let evaluated_command = args[1..].join(" ");
     let mut buffer = Terminator::new(evaluated_command);
     if buffer.is_terminated() {
@@ -263,9 +305,17 @@ fn builtin_eval(args: &[&str], shell: &mut Shell) -> i32 {
     }
 }
 
-fn builtin_history(args: &[&str], shell: &mut Shell) -> i32 { shell.print_history(args) }
+fn builtin_history(args: &[&str], shell: &mut Shell) -> i32 { 
+    if check_help(args, MAN_HISTORY) {
+        return SUCCESS
+    }
+    shell.print_history(args)
+}
 
 fn builtin_source(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_SOURCE) {
+        return SUCCESS
+    }
     match source(shell, args) {
         Ok(()) => SUCCESS,
         Err(why) => {
@@ -278,6 +328,9 @@ fn builtin_source(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_echo(args: &[&str], _: &mut Shell) -> i32 {
+    if check_help(args, MAN_ECHO) {
+        return SUCCESS
+    }
     match echo(args) {
         Ok(()) => SUCCESS,
         Err(why) => {
@@ -290,6 +343,9 @@ fn builtin_echo(args: &[&str], _: &mut Shell) -> i32 {
 }
 
 fn builtin_test(args: &[&str], _: &mut Shell) -> i32 {
+    if check_help(args, MAN_TEST) {
+        return SUCCESS
+    }
     match test(args) {
         Ok(true) => SUCCESS,
         Ok(false) => FAILURE,
@@ -302,6 +358,7 @@ fn builtin_test(args: &[&str], _: &mut Shell) -> i32 {
     }
 }
 
+// TODO create manpage.
 fn builtin_calc(args: &[&str], _: &mut Shell) -> i32 {
     match calc::calc(&args[1..]) {
         Ok(()) => SUCCESS,
@@ -315,6 +372,9 @@ fn builtin_calc(args: &[&str], _: &mut Shell) -> i32 {
 }
 
 fn builtin_random(args: &[&str], _: &mut Shell) -> i32 {
+    if check_help(args, MAN_RANDOM) {
+        return SUCCESS
+    }
     match random::random(&args[1..]) {
         Ok(()) => SUCCESS,
         Err(why) => {
@@ -326,30 +386,59 @@ fn builtin_random(args: &[&str], _: &mut Shell) -> i32 {
     }
 }
 
-fn builtin_true(_: &[&str], _: &mut Shell) -> i32 { SUCCESS }
+fn builtin_true(args: &[&str], _: &mut Shell) -> i32 { 
+    check_help(args, MAN_TRUE);
+    SUCCESS
+}
 
-fn builtin_false(_: &[&str], _: &mut Shell) -> i32 { FAILURE }
+fn builtin_false(args: &[&str], _: &mut Shell) -> i32 {
+    if check_help(args, MAN_FALSE) {
+        return SUCCESS
+    }
+    FAILURE 
+}
 
+// TODO create a manpage
 fn builtin_wait(_: &[&str], shell: &mut Shell) -> i32 {
     shell.wait_for_background();
     SUCCESS
 }
 
-fn builtin_jobs(_: &[&str], shell: &mut Shell) -> i32 {
+fn builtin_jobs(args: &[&str], shell: &mut Shell) -> i32 {
+    check_help(args, MAN_JOBS);
     job_control::jobs(shell);
     SUCCESS
 }
 
-fn builtin_bg(args: &[&str], shell: &mut Shell) -> i32 { job_control::bg(shell, &args[1..]) }
+fn builtin_bg(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_BG) {
+        return SUCCESS
+    }
+    job_control::bg(shell, &args[1..])
+}
 
-fn builtin_fg(args: &[&str], shell: &mut Shell) -> i32 { job_control::fg(shell, &args[1..]) }
+fn builtin_fg(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_FG) {
+        return SUCCESS
+    }
+    job_control::fg(shell, &args[1..]) 
+}
 
-fn builtin_suspend(_: &[&str], _: &mut Shell) -> i32 {
+fn builtin_suspend(args: &[&str], _: &mut Shell) -> i32 {
+    if check_help(args, MAN_SUSPEND) {
+        return SUCCESS
+    }
     shell::signals::suspend(0);
     SUCCESS
 }
 
 fn builtin_disown(args: &[&str], shell: &mut Shell) -> i32 {
+    for arg in args {
+        if *arg == "--help" {
+            print_man(MAN_DISOWN);
+            return SUCCESS
+        }
+    }
     match job_control::disown(shell, &args[1..]) {
         Ok(()) => SUCCESS,
         Err(err) => {
@@ -388,6 +477,9 @@ fn builtin_help(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_exit(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_EXIT) {
+        return SUCCESS
+    }
     // Kill all active background tasks before exiting the shell.
     for process in shell.background.lock().unwrap().iter() {
         if process.state != ProcessState::Empty {
@@ -400,6 +492,9 @@ fn builtin_exit(args: &[&str], shell: &mut Shell) -> i32 {
 
 use regex::Regex;
 fn builtin_matches(args: &[&str], _: &mut Shell) -> i32 {
+    if check_help(args, MAN_MATCHES) {
+        return SUCCESS
+    }
     if args[1..].len() != 2 {
         let stderr = io::stderr();
         let mut stderr = stderr.lock();
@@ -432,6 +527,9 @@ fn args_to_pipeline(args: &[&str]) -> Pipeline {
 }
 
 fn builtin_not(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_NOT) {
+        return SUCCESS
+    }
     shell.run_pipeline(&mut args_to_pipeline(&args[1..]));
     match shell.previous_status {
         SUCCESS => FAILURE,
@@ -441,6 +539,9 @@ fn builtin_not(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_and(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_AND) {
+        return SUCCESS
+    }
     match shell.previous_status {
         SUCCESS => {
             shell.run_pipeline(&mut args_to_pipeline(&args[1..]));
@@ -451,6 +552,9 @@ fn builtin_and(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_or(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_OR) {
+        return SUCCESS
+    }
     match shell.previous_status {
         FAILURE => {
             shell.run_pipeline(&mut args_to_pipeline(&args[1..]));
@@ -461,6 +565,9 @@ fn builtin_or(args: &[&str], shell: &mut Shell) -> i32 {
 }
 
 fn builtin_exists(args: &[&str], shell: &mut Shell) -> i32 {
+    if check_help(args, MAN_EXISTS) {
+        return SUCCESS
+    }
     match exists(args, shell) {
         Ok(true) => SUCCESS,
         Ok(false) => FAILURE,
@@ -474,6 +581,10 @@ 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
+    }
+
     if args[1..].len() != 1 {
         let stderr = io::stderr();
         let mut stderr = stderr.lock();
diff --git a/src/builtins/set.rs b/src/builtins/set.rs
index 607b72f2..815ae5cb 100644
--- a/src/builtins/set.rs
+++ b/src/builtins/set.rs
@@ -4,30 +4,6 @@ use shell::flags::*;
 use std::io::{self, Write};
 use std::iter;
 
-const HELP: &'static str = r#"NAME
-    set - Set or unset values of shell options and positional parameters.
-
-SYNOPSIS
-    set [ --help ] [-e | +e] [-x | +x] [-o [vi | emacs]] [- | --] [STRING]...
-
-DESCRIPTION
-    Shell options may be set using the '-' character, and unset using the '+' character.
-
-OPTIONS
-    -e  Exit immediately if a command exits with a non-zero status.
-
-    -o  Specifies that an argument will follow that sets the key map.
-        The keymap argument may be either `vi` or `emacs`.
-
-    -x  Specifies that commands will be printed as they are executed.
-
-    --  Following arguments will be set as positional arguments in the shell.
-        If no argument are supplied, arguments will be unset.
-
-    -   Following arguments will be set as positional arguments in the shell.
-        If no arguments are suppled, arguments will not be unset.
-"#;
-
 enum PositionalArgs {
     UnsetIfNone,
     RetainIfNone,
@@ -36,7 +12,6 @@ enum PositionalArgs {
 use self::PositionalArgs::*;
 
 pub(crate) fn set(args: &[&str], shell: &mut Shell) -> i32 {
-    let stdout = io::stdout();
     let stderr = io::stderr();
     let mut args_iter = args.iter();
     let mut positionals = None;
@@ -47,12 +22,8 @@ pub(crate) fn set(args: &[&str], shell: &mut Shell) -> i32 {
                 positionals = Some(UnsetIfNone);
                 break;
             }
-            if &arg[2..] == "help" {
-                let mut stdout = stdout.lock();
-                let _ = stdout.write(HELP.as_bytes());
-            } else {
-                return 0;
-            }
+            return 0;
+            
         } else if arg.starts_with('-') {
             if arg.len() == 1 {
                 positionals = Some(RetainIfNone);
diff --git a/src/builtins/status.rs b/src/builtins/status.rs
index 62da25ad..55d0ac87 100644
--- a/src/builtins/status.rs
+++ b/src/builtins/status.rs
@@ -1,9 +1,8 @@
-use std::env;
-use std::error::Error;
-use std::io::{stdout, Write};
-
+use builtins::man_pages::{print_man, MAN_STATUS};
 use shell::Shell;
 
+use std::env;
+
 bitflags! {
     struct Flags : u8 {
         const HELP = 1;
@@ -13,24 +12,6 @@ bitflags! {
     }
 }
 
-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();
@@ -80,7 +61,7 @@ pub(crate) fn status(args: &[&str], shell: &mut Shell) -> Result<(), String> {
         }
 
         if flags.contains(Flags::FILENAME) {
-            // TODO: This technique will not work if ion is renamed.
+            // TODO: This 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();
@@ -92,14 +73,8 @@ pub(crate) fn status(args: &[&str], shell: &mut Shell) -> Result<(), String> {
             }
         }
 
-        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()),
-            };
+            print_man(MAN_STATUS);
         }
     }
     Ok(())
diff --git a/src/builtins/test.rs b/src/builtins/test.rs
index 7346a6ce..36ceff26 100644
--- a/src/builtins/test.rs
+++ b/src/builtins/test.rs
@@ -1,131 +1,16 @@
 use smallstring::SmallString;
-use std::error::Error;
 use std::fs;
-use std::io::{self, BufWriter};
 use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
 use std::path::Path;
 use std::time::SystemTime;
 
-const MAN_PAGE: &'static str = r#"NAME
-    test - perform tests on files and text
-
-SYNOPSIS
-    test [EXPRESSION]
-
-DESCRIPTION
-    Tests the expressions given and returns an exit status of 0 if true, else 1.
-
-OPTIONS
-    -n STRING
-        the length of STRING is nonzero
-
-    STRING
-        equivalent to -n STRING
-
-    -z STRING
-        the length of STRING is zero
-
-    STRING = STRING
-        the strings are equivalent
-
-    STRING != STRING
-        the strings are not equal
-
-    INTEGER -eq INTEGER
-        the integers are equal
-
-    INTEGER -ge INTEGER
-        the first INTEGER is greater than or equal to the first INTEGER
-
-    INTEGER -gt INTEGER
-        the first INTEGER is greater than the first INTEGER
-
-    INTEGER -le INTEGER
-        the first INTEGER is less than or equal to the first INTEGER
-
-    INTEGER -lt INTEGER
-        the first INTEGER is less than the first INTEGER
-
-    INTEGER -ne INTEGER
-        the first INTEGER is not equal to the first INTEGER
-
-    FILE -ef FILE
-        both files have the same device and inode numbers
-
-    FILE -nt FILE
-        the first FILE is newer than the second FILE
-
-    FILE -ot FILE
-        the first file is older than the second FILE
-
-    -b FILE
-        FILE exists and is a block device
-
-    -c FILE
-        FILE exists and is a character device
-
-    -d FILE
-        FILE exists and is a directory
-
-    -e FILE
-        FILE exists
-
-    -f FILE
-        FILE exists and is a regular file
-
-    -h FILE
-        FILE exists and is a symbolic link (same as -L)
-
-    -L FILE
-        FILE exists and is a symbolic link (same as -h)
-
-    -r FILE
-        FILE exists and read permission is granted
-
-    -s FILE
-        FILE exists and has a file size greater than zero
-
-    -S FILE
-        FILE exists and is a socket
-
-    -w FILE
-        FILE exists and write permission is granted
-
-    -x FILE
-        FILE exists and execute (or search) permission is granted
-
-EXAMPLES
-    Test if the file exists:
-        test -e FILE && echo "The FILE exists" || echo "The FILE does not exist"
-
-    Test if the file exists and is a regular file, and if so, write to it:
-        test -f FILE && echo "Hello, FILE" >> FILE || echo "Cannot write to a directory"
-
-    Test if 10 is greater than 5:
-        test 10 -gt 5 && echo "10 is greater than 5" || echo "10 is not greater than 5"
-
-    Test if the user is running a 64-bit OS (POSIX environment only):
-        test $(getconf LONG_BIT) = 64 && echo "64-bit OS" || echo "32-bit OS"
-
-AUTHOR
-    Written by Michael Murphy.
-"#; // @MANEND
-
 pub(crate) fn test(args: &[&str]) -> Result<bool, String> {
-    let stdout = io::stdout();
-    let mut buffer = BufWriter::new(stdout.lock());
-
     let arguments = &args[1..];
-    evaluate_arguments(arguments, &mut buffer)
+    evaluate_arguments(arguments)
 }
 
-fn evaluate_arguments<W: io::Write>(arguments: &[&str], buffer: &mut W) -> Result<bool, String> {
+fn evaluate_arguments(arguments: &[&str]) -> Result<bool, String> {
     match arguments.first() {
-        Some(&"--help") => {
-            buffer.write_all(MAN_PAGE.as_bytes()).map_err(|x| x.description().to_owned())?;
-            buffer.flush().map_err(|x| x.description().to_owned())?;
-            Ok(true)
-        }
         Some(&s) if s.starts_with("-") && s[1..].starts_with(char::is_alphabetic) => {
             // Access the second character in the flag string: this will be type of the flag.
             // If no flag was given, return `SUCCESS`
@@ -354,8 +239,7 @@ fn test_strings() {
 
 #[test]
 fn test_empty_str() {
-    let mut empty = BufWriter::new(io::sink());
-    let mut eval = |args: Vec<&str>| evaluate_arguments(&args, &mut empty);
+    let eval = |args: Vec<&str>| evaluate_arguments(&args);
     assert_eq!(eval(vec![""]), Ok(false));
     assert_eq!(eval(vec!["c", "=", ""]), Ok(false));
 }
@@ -363,48 +247,45 @@ fn test_empty_str() {
 
 #[test]
 fn test_integers_arguments() {
-    let stdout = io::stdout();
-    let mut buffer = BufWriter::new(stdout.lock());
-
     // Equal To
-    assert_eq!(evaluate_arguments(&["10", "-eq", "10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["10", "-eq", "5"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-10", "-eq", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["-10", "-eq", "10"], &mut buffer), Ok(false));
+    assert_eq!(evaluate_arguments(&["10", "-eq", "10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-eq", "5"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-10", "-eq", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["-10", "-eq", "10"]), Ok(false));
 
     // Greater Than or Equal To
-    assert_eq!(evaluate_arguments(&["10", "-ge", "10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["10", "-ge", "5"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["5", "-ge", "10"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-9", "-ge", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["-10", "-ge", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["-10", "-ge", "10"], &mut buffer), Ok(false));
+    assert_eq!(evaluate_arguments(&["10", "-ge", "10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-ge", "5"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["5", "-ge", "10"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-9", "-ge", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["-10", "-ge", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["-10", "-ge", "10"]), Ok(false));
 
     // Less Than or Equal To
-    assert_eq!(evaluate_arguments(&["5", "-le", "5"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["5", "-le", "10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["10", "-le", "5"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-11", "-le", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["-10", "-le", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["10", "-le", "-10"], &mut buffer), Ok(false));
+    assert_eq!(evaluate_arguments(&["5", "-le", "5"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["5", "-le", "10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-le", "5"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-11", "-le", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["-10", "-le", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-le", "-10"]), Ok(false));
 
     // Less Than
-    assert_eq!(evaluate_arguments(&["5", "-lt", "10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["10", "-lt", "5"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-11", "-lt", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["10", "-lt", "-10"], &mut buffer), Ok(false));
+    assert_eq!(evaluate_arguments(&["5", "-lt", "10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-lt", "5"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-11", "-lt", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-lt", "-10"]), Ok(false));
 
     // Greater Than
-    assert_eq!(evaluate_arguments(&["10", "-gt", "5"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["5", "-gt", "10"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-9", "-gt", "-10"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["-10", "-gt", "10"], &mut buffer), Ok(false));
+    assert_eq!(evaluate_arguments(&["10", "-gt", "5"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["5", "-gt", "10"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-9", "-gt", "-10"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["-10", "-gt", "10"]), Ok(false));
 
     // Not Equal To
-    assert_eq!(evaluate_arguments(&["10", "-ne", "5"], &mut buffer), Ok(true));
-    assert_eq!(evaluate_arguments(&["5", "-ne", "5"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-10", "-ne", "-10"], &mut buffer), Ok(false));
-    assert_eq!(evaluate_arguments(&["-10", "-ne", "10"], &mut buffer), Ok(true));
+    assert_eq!(evaluate_arguments(&["10", "-ne", "5"]), Ok(true));
+    assert_eq!(evaluate_arguments(&["5", "-ne", "5"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-10", "-ne", "-10"]), Ok(false));
+    assert_eq!(evaluate_arguments(&["-10", "-ne", "10"]), Ok(true));
 }
 
 #[test]
@@ -441,4 +322,4 @@ fn test_file_has_execute_permission() {
 fn test_file_size_is_greater_than_zero() {
     assert_eq!(file_size_is_greater_than_zero("testing/file_with_text"), true);
     assert_eq!(file_size_is_greater_than_zero("testing/empty_file"), false);
-}
+}
\ No newline at end of file
-- 
GitLab