// file : libbuild2/functions-filesystem.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include <libbutl/filesystem.mxx> #include <libbuild2/function.hxx> #include <libbuild2/variable.hxx> using namespace std; namespace build2 { // Return paths of filesystem entries that match the pattern. See // path_search() overloads (below) for details. // static names path_search (const path& pattern, const optional<dir_path>& start) { names r; auto add = [&r] (path&& p, const std::string&, bool interm) -> bool { // Canonicalizing paths seems to be the right thing to do. Otherwise, we // can end up with different separators in the same path on Windows. // if (!interm) r.emplace_back ( value_traits<path>::reverse (move (p.canonicalize ()))); return true; }; // Print paths "as is" in the diagnostics. // try { if (pattern.absolute ()) path_search (pattern, add); else { // An absolute start directory must be specified for the relative // pattern. // if (!start || start->relative ()) { diag_record dr (fail); if (!start) dr << "start directory is not specified"; else dr << "start directory '" << start->representation () << "' is relative"; dr << info << "pattern '" << pattern.representation () << "' is relative"; } path_search (pattern, add, *start); } } catch (const system_error& e) { diag_record d (fail); d << "unable to scan"; // If the pattern is absolute, then the start directory is not used, and // so printing it would be misleading. // if (start && pattern.relative ()) d << " '" << start->representation () << "'"; d << ": " << e << info << "pattern: '" << pattern.representation () << "'"; } return r; } using butl::path_match; // Return true if a path for a filesystem entry matches the pattern. See // path_match() overloads (below) for details. // static bool path_match (const path& pattern, const path& entry, const optional<dir_path>& start) { // If pattern and entry are both either absolute or relative and // non-empty, and the first pattern component is not a self-matching // wildcard, then ignore the start directory. // bool rel (pattern.relative () == entry.relative () && !pattern.empty () && !entry.empty ()); bool self (!pattern.empty () && (*pattern.begin ()).find ("***") != string::npos); if (rel && !self) return path_match (pattern, entry); // The start directory must be specified and be absolute. // if (!start || start->relative ()) { diag_record dr (fail); // Print paths "as is". // if (!start) dr << "start directory is not specified"; else dr << "start directory path '" << start->representation () << "' is relative"; dr << info << "pattern: '" << pattern.representation () << "'" << info << "entry: '" << entry.representation () << "'"; } return path_match (pattern, entry, *start); } void filesystem_functions (function_map& m) { function_family f (m, "filesystem"); // path_search // // Return filesystem paths that match the pattern. If the pattern is an // absolute path, then the start directory is ignored (if present). // Otherwise, the start directory must be specified and be absolute. // f["path_search"] = [](path pattern, optional<dir_path> start) { return path_search (pattern, start); }; f["path_search"] = [](path pattern, names start) { return path_search (pattern, convert<dir_path> (move (start))); }; f["path_search"] = [](names pattern, optional<dir_path> start) { return path_search (convert<path> (move (pattern)), start); }; f["path_search"] = [](names pattern, names start) { return path_search (convert<path> (move (pattern)), convert<dir_path> (move (start))); }; // path_match // // Match a filesystem entry name against a name pattern (both are strings), // or a filesystem entry path against a path pattern. For the latter case // the start directory may also be required (see below). The semantics of // the pattern and name/entry arguments is determined according to the // following rules: // // - The arguments must be of the string or path types, or be untyped. // // - If one of the arguments is typed, then the other one must be of the // same type or be untyped. In the later case, an untyped argument is // converted to the type of the other argument. // // - If both arguments are untyped and the start directory is specified, // then the arguments are converted to the path type. // // - If both arguments are untyped and the start directory is not // specified, then, if one of the arguments is syntactically a path (the // value contains a directory separator), convert them to the path type, // otherwise to the string type (match as names). // // If pattern and entry paths are both either absolute or relative and // non-empty, and the first pattern component is not a self-matching // wildcard (doesn't contain ***), then the start directory is not // required, and is ignored if specified. Otherwise, the start directory // must be specified and be an absolute path. // // Name matching. // f["path_match"] = [](string pattern, string name) { return path_match (pattern, name); }; // Path matching. // f["path_match"] = [](path pat, path ent, optional<dir_path> start) { return path_match (pat, ent, start); }; // The semantics depends on the presence of the start directory or the // first two argument syntactic representation. // f["path_match"] = [](names pat, names ent, optional<names> start) { auto path_arg = [] (const names& a) -> bool { return a.size () == 1 && (a[0].directory () || a[0].value.find_first_of (path::traits_type::directory_separators) != string::npos); }; return start || path_arg (pat) || path_arg (ent) ? path_match (convert<path> (move (pat)), // Match as paths. convert<path> (move (ent)), start ? convert<dir_path> (move (*start)) : optional<dir_path> ()) : path_match (convert<string> (move (pat)), // Match as strings. convert<string> (move (ent))); }; } }