mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-01 20:10:28 +00:00
Shell: Add where
builtin
The builtin is based on the behaviour of the z-shell. Namely it tries to resolve every argument one by one. When resolving (in the order below) the following results can occur: 1. The argument is a shell built-in command. Then print it. 2. The argument is an alias. In this case we print the mapped value. 3. The argument was found in the `PATH` environment variable. In this case we print the resolved absolute path and try to find more occurences in the `PATH` environment variable. 4. None of the above. If no earlier argument got resolved, we print the error `{argument} not found`. If at least one argument got resolved we exit with exit code 0, otherwise 1. By not using Core::File to resolve the executable in the environment but rather using a modified version of the code we print every matching executable of the given name. This behaviour matches up with the z-shell. The builtin has the following flags to modify the behaviour according to the users needs: - `-p --path-only`: This skips the built-in and alias checks (step 1 & 2) - `-s --follow-symlink`: This follows the symlinks of an executable to its symlink-free location. - `-w --type`: This displays the type of the found object without any additional descriptive information.
This commit is contained in:
parent
8238f926fd
commit
0e26f2657e
Notes:
sideshowbarker
2024-07-17 02:33:04 +09:00
Author: https://github.com/Vetrox 🔰 Commit: https://github.com/SerenityOS/serenity/commit/0e26f2657e Pull-request: https://github.com/SerenityOS/serenity/pull/16660 Reviewed-by: https://github.com/alimpfard
2 changed files with 101 additions and 0 deletions
|
@ -39,6 +39,106 @@ int Shell::builtin_dump(int argc, char const** argv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
enum FollowSymlinks {
|
||||
Yes,
|
||||
No
|
||||
};
|
||||
|
||||
static Vector<DeprecatedString> find_matching_executables_in_path(StringView filename, FollowSymlinks follow_symlinks = FollowSymlinks::No)
|
||||
{
|
||||
// Edge cases in which there are guaranteed no solutions
|
||||
if (filename.is_empty() || filename.contains('/'))
|
||||
return {};
|
||||
|
||||
char const* path_str = getenv("PATH");
|
||||
auto path = DEFAULT_PATH_SV;
|
||||
if (path_str != nullptr) // maybe && *path_str
|
||||
path = { path_str, strlen(path_str) };
|
||||
|
||||
Vector<DeprecatedString> executables;
|
||||
auto directories = path.split_view(':');
|
||||
for (auto directory : directories) {
|
||||
auto file = DeprecatedString::formatted("{}/{}", directory, filename);
|
||||
|
||||
if (follow_symlinks == FollowSymlinks::Yes) {
|
||||
auto path_or_error = Core::File::read_link(file);
|
||||
if (!path_or_error.is_error())
|
||||
file = path_or_error.release_value();
|
||||
}
|
||||
if (access(file.characters(), X_OK) == 0)
|
||||
executables.append(move(file));
|
||||
}
|
||||
|
||||
return executables;
|
||||
}
|
||||
|
||||
int Shell::builtin_where(int argc, char const** argv)
|
||||
{
|
||||
Vector<StringView> arguments;
|
||||
bool do_only_path_search { false };
|
||||
bool do_follow_symlinks { false };
|
||||
bool do_print_only_type { false };
|
||||
|
||||
Core::ArgsParser parser;
|
||||
parser.add_positional_argument(arguments, "List of shell builtins, aliases or executables", "arguments");
|
||||
parser.add_option(do_only_path_search, "Search only for executables in the PATH environment variable", "path-only", 'p');
|
||||
parser.add_option(do_follow_symlinks, "Follow symlinks and print the symlink free path", "follow-symlink", 's');
|
||||
parser.add_option(do_print_only_type, "Print the argument type instead of a human readable description", "type", 'w');
|
||||
|
||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||
return 1;
|
||||
|
||||
auto const lookup_alias = [do_only_path_search, &m_aliases = this->m_aliases](StringView alias) -> Optional<DeprecatedString> {
|
||||
if (do_only_path_search)
|
||||
return {};
|
||||
return m_aliases.get(alias);
|
||||
};
|
||||
|
||||
auto const lookup_builtin = [do_only_path_search](StringView builtin) -> Optional<DeprecatedString> {
|
||||
if (do_only_path_search)
|
||||
return {};
|
||||
for (auto const& _builtin : builtin_names) {
|
||||
if (_builtin == builtin) {
|
||||
return builtin;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
bool at_least_one_succeded { false };
|
||||
for (auto const& argument : arguments) {
|
||||
auto const alias = lookup_alias(argument);
|
||||
if (alias.has_value()) {
|
||||
if (do_print_only_type)
|
||||
outln("{}: alias", argument);
|
||||
else
|
||||
outln("{}: aliased to {}", argument, alias.value());
|
||||
at_least_one_succeded = true;
|
||||
}
|
||||
|
||||
auto const builtin = lookup_builtin(argument);
|
||||
if (builtin.has_value()) {
|
||||
if (do_print_only_type)
|
||||
outln("{}: builtin", builtin.value());
|
||||
else
|
||||
outln("{}: shell built-in command", builtin.value());
|
||||
at_least_one_succeded = true;
|
||||
}
|
||||
|
||||
auto const executables = find_matching_executables_in_path(argument, do_follow_symlinks ? FollowSymlinks::Yes : FollowSymlinks::No);
|
||||
for (auto const& path : executables) {
|
||||
if (do_print_only_type)
|
||||
outln("{}: command", argument);
|
||||
else
|
||||
outln(path);
|
||||
at_least_one_succeded = true;
|
||||
}
|
||||
if (!at_least_one_succeded)
|
||||
warnln("{} not found", argument);
|
||||
}
|
||||
return at_least_one_succeded ? 0 : 1;
|
||||
}
|
||||
|
||||
int Shell::builtin_alias(int argc, char const** argv)
|
||||
{
|
||||
Vector<DeprecatedString> arguments;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#define ENUMERATE_SHELL_BUILTINS() \
|
||||
__ENUMERATE_SHELL_BUILTIN(alias) \
|
||||
__ENUMERATE_SHELL_BUILTIN(where) \
|
||||
__ENUMERATE_SHELL_BUILTIN(cd) \
|
||||
__ENUMERATE_SHELL_BUILTIN(cdh) \
|
||||
__ENUMERATE_SHELL_BUILTIN(pwd) \
|
||||
|
|
Loading…
Reference in a new issue