// file : tests/wildcard/driver.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include <cassert> #ifndef __cpp_lib_modules_ts #include <map> #include <string> #include <vector> #include <algorithm> // sort() #include <exception> #include <iostream> #endif // Other includes. #ifdef __cpp_modules_ts #ifdef __cpp_lib_modules_ts import std.core; import std.io; #endif import butl.path; import butl.utility; // operator<<(ostream, exception) import butl.optional; import butl.filesystem; import butl.path_pattern; #else #include <libbutl/path.mxx> #include <libbutl/utility.mxx> #include <libbutl/optional.mxx> #include <libbutl/filesystem.mxx> #include <libbutl/path-pattern.mxx> #endif using namespace std; using namespace butl; // Disable arguments globbing that may be enabled by default for MinGW runtime. // // Note that if _CRT_glob symbol is not defined explicitly, then runtime will // be bound to the one defined in the implicitly linked libmingw32.a library. // Yet another (but more troublesome) way is to link CRT_noglob.o (from MinGW // libraries directory) that provides exactly the same symbol definition. // #ifdef __MINGW32__ int _CRT_glob = 0; #endif // Usages: // // argv[0] -mn <name> <pattern> // argv[0] -sd [-i] [-n] <pattern> [<dir>] // argv[0] -sp [-i] [-n] <path> <pattern> [<dir>] // // Execute actions specified by the first option. Exit with code 0 if succeed, // 1 if fail, 2 on the underlying OS error (print error description to STDERR). // // -mn // Match a name against the pattern. // // -sd // Search for paths matching the pattern in the directory specified (absent // directory means the current one). Print the matching canonicalized paths // to STDOUT in the ascending order. Succeed if at least one matching path // is found. For each matching path we will assert that it is also get // matched being searched in the directory tree represented by this path // itself. // // Note that the driver excludes from search file system entries which names // start from dot, unless the pattern explicitly matches them. // // -sp // Same as above, but behaves as if the directory tree being searched // through contains only the specified entry. The start directory is used if // the first pattern component is a self-matching wildcard. // // -i // Pass psflags::ignorable_components to the match/search functions. // Meaningful in combination with -sd or -sp options and must follow it, if // specified in the command line. // // -n // Do not sort paths found. Meaningful in combination with -sd or -sp // options and must follow it, if specified in the command line. // int main (int argc, const char* argv[]) try { using butl::optional; assert (argc >= 2); string op (argv[1]); if (op == "-mn") { assert (argc == 4); string name (argv[2]); string pattern (argv[3]); return path_match (name, pattern) ? 0 : 1; } else if (op == "-sd" || op == "-sp") { assert (argc >= (op == "-sd" ? 3 : 4)); bool sort (true); path_match_flags flags (path_match_flags::follow_symlinks); int i (2); for (; i != argc; ++i) { string o (argv[i]); if (o == "-n") sort = false; else if (o == "-i") flags |= path_match_flags::match_absent; else break; // End of options. } optional<path> entry; if (op == "-sp") { assert (i != argc); entry = path (argv[i++]); } assert (i != argc); // Still need pattern. path pattern (argv[i++]); dir_path start; if (i != argc) start = dir_path (argv[i++]); assert (i == argc); // All args parsed, vector<path> paths; map<path, size_t> path_count; auto add = [&paths, &path_count, &start] (path&& p, const string& pt, bool interim) { bool pd (!pt.empty () && pt[0] == '.'); // Dot-started pattern. const path& fp (!p.empty () ? p : path_cast<path> (!start.empty () ? start : path::current_directory ())); const string& s (fp.leaf ().string ()); assert (!s.empty ()); bool ld (s[0] == '.'); // Dot-started leaf. // Skip dot-started names if pattern is not dot-started. // bool skip (ld && !pd); if (interim) return !skip; if (!skip) { p.canonicalize (); auto i (path_count.find (p)); if (i == path_count.end ()) path_count[p] = 1; else ++(i->second); paths.emplace_back (move (p)); } return true; }; if (!entry) path_search (pattern, add, start, flags); else path_search (pattern, *entry, add, start, flags); // It the search succeeds, then test search in the directory tree // represented by each matched path. Otherwise, if the directory tree is // specified, then make sure that it doesn't match the pattern. // if (!path_count.empty ()) { for (const auto& p: path_count) { // Will match multiple times if the pattern contains several recursive // components. // size_t match_count (0); auto check = [&p, &match_count, flags] (path&& pe, const string&, bool inter) { if (pe == p.first) { if (!inter) ++match_count; else if ((flags & path_match_flags::match_absent) == path_match_flags::none) // For self-matching the callback is first called in the interim // mode (through the preopen function) with an empty path. // assert (pe.empty ()); } return true; }; path_search (pattern, p.first, check, start, flags); assert (match_count == p.second); // Test path match. // assert (path_match (p.first, pattern, start, flags)); } } else if (entry) assert (!path_match (*entry, pattern, start, flags)); // Print the found paths. // if (sort) std::sort (paths.begin (), paths.end ()); for (const auto& p: paths) cout << p.representation () << endl; return paths.empty () ? 1 : 0; } else assert (false); } catch (const invalid_path& e) { cerr << e << ": " << e.path << endl; return 2; } catch (const exception& e) { cerr << e << endl; return 2; }