diff options
-rw-r--r-- | Userland/Shell/Builtin.cpp | 71 | ||||
-rw-r--r-- | Userland/Shell/Shell.h | 1 | ||||
-rw-r--r-- | Userland/Shell/Tests/builtin-test.sh | 24 |
3 files changed, 96 insertions, 0 deletions
diff --git a/Userland/Shell/Builtin.cpp b/Userland/Shell/Builtin.cpp index 8e90a3fc49..c291f6896b 100644 --- a/Userland/Shell/Builtin.cpp +++ b/Userland/Shell/Builtin.cpp @@ -26,6 +26,7 @@ #include "AST.h" #include "Shell.h" +#include "Shell/Formatter.h" #include <AK/LexicalPath.h> #include <AK/ScopeGuard.h> #include <LibCore/ArgsParser.h> @@ -127,6 +128,76 @@ int Shell::builtin_bg(int argc, const char** argv) return 0; } +int Shell::builtin_type(int argc, const char** argv) +{ + + Vector<const char*> commands; + bool dont_show_function_source = false; + + Core::ArgsParser parser; + parser.set_general_help("Display information about commands."); + parser.add_positional_argument(commands, "Command(s) to list info about", "command"); + parser.add_option(dont_show_function_source, "Do not show functions source.", "no-fn-source", 'f'); + + if (!parser.parse(argc, const_cast<char**>(argv), false)) + return 1; + + bool something_not_found = false; + + for (auto& command : commands) { + // check if it is an alias + if (auto alias = m_aliases.get(command); alias.has_value()) { + printf("%s is aliased to `%s`\n", escape_token(command).characters(), escape_token(alias.value()).characters()); + continue; + } + + // check if it is a function + if (auto function = m_functions.get(command); function.has_value()) { + auto fn = function.value(); + printf("%s is a function\n", command); + if (!dont_show_function_source) { + StringBuilder builder; + builder.append(fn.name); + builder.append("("); + for (size_t i = 0; i < fn.arguments.size(); i++) { + builder.append(fn.arguments[i]); + if (!(i == fn.arguments.size() - 1)) + builder.append(" "); + } + builder.append(") {\n"); + if (fn.body) { + auto formatter = Formatter(*fn.body); + builder.append(formatter.format()); + printf("%s\n}\n", builder.build().characters()); + } else { + printf("%s\n}\n", builder.build().characters()); + } + } + continue; + } + + // check if its a builtin + if (has_builtin(command)) { + printf("%s is a shell builtin\n", command); + continue; + } + + // check if its an executable in PATH + auto fullpath = Core::find_executable_in_path(command); + if (!fullpath.is_null()) { + printf("%s is %s\n", command, escape_token(fullpath).characters()); + continue; + } + something_not_found = true; + printf("type: %s not found\n", command); + } + + if (something_not_found) + return 1; + else + return 0; +} + int Shell::builtin_cd(int argc, const char** argv) { const char* arg_path = nullptr; diff --git a/Userland/Shell/Shell.h b/Userland/Shell/Shell.h index 832bc8d723..194538f0ae 100644 --- a/Userland/Shell/Shell.h +++ b/Userland/Shell/Shell.h @@ -45,6 +45,7 @@ __ENUMERATE_SHELL_BUILTIN(cd) \ __ENUMERATE_SHELL_BUILTIN(cdh) \ __ENUMERATE_SHELL_BUILTIN(pwd) \ + __ENUMERATE_SHELL_BUILTIN(type) \ __ENUMERATE_SHELL_BUILTIN(exec) \ __ENUMERATE_SHELL_BUILTIN(exit) \ __ENUMERATE_SHELL_BUILTIN(export) \ diff --git a/Userland/Shell/Tests/builtin-test.sh b/Userland/Shell/Tests/builtin-test.sh new file mode 100644 index 0000000000..f42761bde2 --- /dev/null +++ b/Userland/Shell/Tests/builtin-test.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +source $(dirname "$0")/test-commons.inc + + +if not [ "$(type ls)" = "ls is $(which ls)" ] { fail "'type' on a binary not working" } + +if not [ "$(type pwd)" = "pwd is a shell builtin" ] { fail "'type' on a builtin not working" } + +f() { ls } + +if not [ "$(type f)" = "f is a function f() { ls }" ] { fail "'type' on a function not working" } + + +if not [ "$(type f -f)" = "f is a function" ] { fail "'type' on a function not working with -f" } + +alias l=ls + +if not [ "$(type l)" = "l is aliased to `ls`" ] { fail "'type' on a alias not working" } + + +if not [ "$(type l ls)" = "l is aliased to `ls` ls is $(which ls)" ] { fail "'type' on multiple commands not working" } + +echo PASS |