Quellcode durchsuchen

Shell: Allow * and ? wildcard expansion in arguments

Should also presumably allow for escaping and such, but this is a start.
Fixes #112.
Robin Burchell vor 6 Jahren
Ursprung
Commit
9947ee9566
3 geänderte Dateien mit 115 neuen und 2 gelöschten Zeilen
  1. 7 0
      AK/AKString.h
  2. 54 0
      AK/String.cpp
  3. 54 2
      Shell/main.cpp

+ 7 - 0
AK/AKString.h

@@ -61,7 +61,13 @@ public:
     {
     }
 
+    enum class CaseSensitivity {
+        CaseInsensitive,
+        CaseSensitive,
+    };
+
     static String repeated(char, int count);
+    bool matches(const String& pattern, CaseSensitivity = CaseSensitivity::CaseInsensitive) const;
 
     int to_int(bool& ok) const;
     unsigned to_uint(bool& ok) const;
@@ -136,6 +142,7 @@ public:
     StringView view() const { return { characters(), length() }; }
 
 private:
+    bool match_helper(const String& mask) const;
     RetainPtr<StringImpl> m_impl;
 };
 

+ 54 - 0
AK/String.cpp

@@ -196,4 +196,58 @@ String String::repeated(char ch, int count)
     return *impl;
 }
 
+bool String::matches(const String& mask, CaseSensitivity case_sensitivity) const
+{
+    if (case_sensitivity == CaseSensitivity::CaseInsensitive) {
+        String this_lower = this->to_lowercase();
+        String mask_lower = mask.to_lowercase();
+        return this_lower.match_helper(mask_lower);
+    }
+
+    return match_helper(mask);
+}
+
+bool String::match_helper(const String& mask) const
+{
+    if (is_null() || mask.is_null())
+        return false;
+
+    const char* string_ptr = characters();
+    const char* mask_ptr = mask.characters();
+
+    // Match string against mask directly unless we hit a *
+    while ((*string_ptr) && (*mask_ptr != '*')) {
+        if ((*mask_ptr != *string_ptr) && (*mask_ptr != '?'))
+            return false;
+        mask_ptr++;
+        string_ptr++;
+    }
+
+    const char* cp = nullptr;
+    const char* mp = nullptr;
+
+    while (*string_ptr) {
+        if (*mask_ptr == '*') {
+            // If we have only a * left, there is no way to not match.
+            if (!*++mask_ptr)
+                return true;
+            mp = mask_ptr;
+            cp = string_ptr+1;
+        } else if ((*mask_ptr == *string_ptr) || (*mask_ptr == '?')) {
+            mask_ptr++;
+            string_ptr++;
+        } else {
+            mask_ptr = mp;
+            string_ptr = cp++;
+        }
+    }
+
+    // Handle any trailing mask
+    while (*mask_ptr == '*')
+        mask_ptr++;
+
+    // If we 'ate' all of the mask then we match.
+    return !*mask_ptr;
+}
+
 }

+ 54 - 2
Shell/main.cpp

@@ -12,6 +12,7 @@
 #include <sys/utsname.h>
 #include <AK/FileSystemPath.h>
 #include <LibCore/CElapsedTimer.h>
+#include <LibCore/CDirIterator.h>
 #include "GlobalState.h"
 #include "Parser.h"
 #include "LineEditor.h"
@@ -223,6 +224,47 @@ struct CommandTimer {
     CElapsedTimer timer;
 };
 
+static Vector<String> process_arguments(const Vector<String>& args)
+{
+    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;
+            }
+        }
+
+        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;
+}
+
 static int run_command(const String& cmd)
 {
     if (cmd.is_empty())
@@ -330,11 +372,21 @@ static int run_command(const String& cmd)
 
     for (int i = 0; i < subcommands.size(); ++i) {
         auto& subcommand = subcommands[i];
+        Vector<String> argv_string = process_arguments(subcommand.args);
         Vector<const char*> argv;
-        for (auto& arg : subcommand.args)
-            argv.append(arg.characters());
+        argv.ensure_capacity(argv_string.size());
+        for (const auto& s : argv_string) {
+            argv.append(s.characters());
+        }
         argv.append(nullptr);
 
+#ifdef SH_DEBUG
+        for (auto& arg : argv) {
+            dbgprintf("<%s> ", arg);
+        }
+        dbgprintf("\n");
+#endif
+
         int retval = 0;
         if (handle_builtin(argv.size() - 1, const_cast<char**>(argv.data()), retval))
             return retval;