summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Shell/Builtin.cpp71
-rw-r--r--Userland/Shell/Shell.h1
-rw-r--r--Userland/Shell/Tests/builtin-test.sh24
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