mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:47:45 +00:00
Shell: Implement more advanced globbing.
A glob has to be resolved against the directory corresponding to the part of the path it is found in, not the current directory. For example, in /usr/i*/AK/, the glob has to be resolved inside /usr. Moreover, an argument can contain more than one glob, such as /u*/*/?, in which case they have to be resolved recursively. In case a glob matches nothing, the argument should be used as is.
This commit is contained in:
parent
d211307547
commit
802612f665
1 changed files with 99 additions and 32 deletions
131
Shell/main.cpp
131
Shell/main.cpp
|
@ -2,6 +2,7 @@
|
||||||
#include "LineEditor.h"
|
#include "LineEditor.h"
|
||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
#include <AK/FileSystemPath.h>
|
#include <AK/FileSystemPath.h>
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibCore/CDirIterator.h>
|
#include <LibCore/CDirIterator.h>
|
||||||
#include <LibCore/CElapsedTimer.h>
|
#include <LibCore/CElapsedTimer.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -219,42 +220,108 @@ struct CommandTimer {
|
||||||
CElapsedTimer timer;
|
CElapsedTimer timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool is_glob(const StringView& s)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < s.length(); i++) {
|
||||||
|
char c = s.characters()[i];
|
||||||
|
if (c == '*' || c == '?')
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector<StringView> split_path(const StringView &path)
|
||||||
|
{
|
||||||
|
Vector<StringView> parts;
|
||||||
|
|
||||||
|
ssize_t substart = 0;
|
||||||
|
for (ssize_t i = 0; i < path.length(); i++) {
|
||||||
|
char ch = path.characters()[i];
|
||||||
|
if (ch != '/')
|
||||||
|
continue;
|
||||||
|
ssize_t sublen = i - substart;
|
||||||
|
if (sublen != 0)
|
||||||
|
parts.append(path.substring_view(substart, sublen));
|
||||||
|
parts.append(path.substring_view(i, 1));
|
||||||
|
substart = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t taillen = path.length() - substart;
|
||||||
|
if (taillen != 0)
|
||||||
|
parts.append(path.substring_view(substart, taillen));
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector<String> expand_globs(const StringView& path, const StringView& base)
|
||||||
|
{
|
||||||
|
auto parts = split_path(path);
|
||||||
|
|
||||||
|
StringBuilder builder;
|
||||||
|
builder.append(base);
|
||||||
|
Vector<String> res;
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.size(); ++i) {
|
||||||
|
auto& part = parts[i];
|
||||||
|
if (!is_glob(part)) {
|
||||||
|
builder.append(part);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found a glob.
|
||||||
|
String new_base = builder.to_string();
|
||||||
|
StringView new_base_v = new_base;
|
||||||
|
if (new_base_v.is_empty())
|
||||||
|
new_base_v = ".";
|
||||||
|
CDirIterator di(new_base_v, CDirIterator::NoFlags);
|
||||||
|
|
||||||
|
if (di.has_error()) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (di.has_next()) {
|
||||||
|
String name = di.next_path();
|
||||||
|
|
||||||
|
// Dotfiles have to be explicitly requested
|
||||||
|
if (name[0] == '.' && part[0] != '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// And even if they are, skip . and ..
|
||||||
|
if (name == "." || name == "..")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (name.matches(part, String::CaseSensitivity::CaseSensitive)) {
|
||||||
|
|
||||||
|
StringBuilder nested_base;
|
||||||
|
nested_base.append(new_base);
|
||||||
|
nested_base.append(name);
|
||||||
|
|
||||||
|
StringView remaining_path = path.substring_view_starting_after_substring(part);
|
||||||
|
Vector<String> nested_res = expand_globs(remaining_path, nested_base.to_string());
|
||||||
|
for (auto& s : nested_res)
|
||||||
|
res.append(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found no globs.
|
||||||
|
String new_path = builder.to_string();
|
||||||
|
if (access(new_path.characters(), F_OK) == 0)
|
||||||
|
res.append(new_path);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static Vector<String> process_arguments(const Vector<String>& args)
|
static Vector<String> process_arguments(const Vector<String>& args)
|
||||||
{
|
{
|
||||||
Vector<String> argv_string;
|
Vector<String> argv_string;
|
||||||
for (auto& arg : args) {
|
for (auto& arg : args) {
|
||||||
bool is_glob = false;
|
auto expanded = expand_globs(arg, "");
|
||||||
for (int i = 0; i < arg.length(); i++) {
|
if (expanded.is_empty())
|
||||||
char c = arg.characters()[i];
|
argv_string.append(arg);
|
||||||
if (c == '*' || c == '?') {
|
else
|
||||||
is_glob = true;
|
for (auto& path : expand_globs(arg, ""))
|
||||||
}
|
argv_string.append(path);
|
||||||
}
|
|
||||||
|
|
||||||
if (is_glob == false) {
|
|
||||||
argv_string.append(arg.characters());
|
|
||||||
} else {
|
|
||||||
CDirIterator di(".", CDirIterator::NoFlags);
|
|
||||||
if (di.has_error()) {
|
|
||||||
fprintf(stderr, "CDirIterator: %s\n", di.error_string());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (di.has_next()) {
|
|
||||||
String name = di.next_path();
|
|
||||||
|
|
||||||
// Dotfiles have to be explicitly requested
|
|
||||||
if (name[0] == '.' && arg[0] != '.')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// And even if they are, skip . and ..
|
|
||||||
if (name == "." || name == "..")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (name.matches(arg, String::CaseSensitivity::CaseSensitive))
|
|
||||||
argv_string.append(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return argv_string;
|
return argv_string;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue