Bläddra i källkod

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.
Sergey Bugaev 6 år sedan
förälder
incheckning
802612f665
1 ändrade filer med 93 tillägg och 26 borttagningar
  1. 93 26
      Shell/main.cpp

+ 93 - 26
Shell/main.cpp

@@ -2,6 +2,7 @@
 #include "LineEditor.h"
 #include "Parser.h"
 #include <AK/FileSystemPath.h>
+#include <AK/StringBuilder.h>
 #include <LibCore/CDirIterator.h>
 #include <LibCore/CElapsedTimer.h>
 #include <errno.h>
@@ -219,42 +220,108 @@ struct CommandTimer {
     CElapsedTimer timer;
 };
 
-static Vector<String> process_arguments(const Vector<String>& args)
+static bool is_glob(const StringView& s)
 {
-    Vector<String> argv_string;
-    for (auto& arg : args) {
-        bool is_glob = false;
-        for (int i = 0; i < arg.length(); i++) {
-            char c = arg.characters()[i];
-            if (c == '*' || c == '?') {
-                is_glob = true;
-            }
+    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;
         }
 
-        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());
+        while (di.has_next()) {
+            String name = di.next_path();
+
+            // Dotfiles have to be explicitly requested
+            if (name[0] == '.' && part[0] != '.')
                 continue;
-            }
 
-            while (di.has_next()) {
-                String name = di.next_path();
+            // And even if they are, skip . and ..
+            if (name == "." || name == "..")
+                continue;
 
-                // Dotfiles have to be explicitly requested
-                if (name[0] == '.' && arg[0] != '.')
-                    continue;
+            if (name.matches(part, String::CaseSensitivity::CaseSensitive)) {
 
-                // And even if they are, skip . and ..
-                if (name == "." || name == "..")
-                    continue;
+                StringBuilder nested_base;
+                nested_base.append(new_base);
+                nested_base.append(name);
 
-                if (name.matches(arg, String::CaseSensitivity::CaseSensitive))
-                    argv_string.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)
+{
+    Vector<String> argv_string;
+    for (auto& arg : args) {
+        auto expanded = expand_globs(arg, "");
+        if (expanded.is_empty())
+            argv_string.append(arg);
+        else
+            for (auto& path : expand_globs(arg, ""))
+                argv_string.append(path);
     }
 
     return argv_string;