From 3e6110dec6f4cb004b8594b9b798a9db5b08fe7a Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 7 Dec 2016 01:22:53 +0300 Subject: Add path::current(), path::parent() --- butl/filesystem.cxx | 18 +++++++++++------ butl/path | 50 ++++++++++++++++++++++++++++++++++++++++++------ butl/path.cxx | 16 ++++++++-------- butl/path.ixx | 16 +++++++++++++++- butl/path.txx | 38 +++++++++++++----------------------- butl/process.cxx | 10 +++++----- tests/path/driver.cxx | 17 ++++++++++++---- tests/process/driver.cxx | 6 +++--- 8 files changed, 113 insertions(+), 58 deletions(-) diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx index 424e361..0643938 100644 --- a/butl/filesystem.cxx +++ b/butl/filesystem.cxx @@ -531,14 +531,17 @@ namespace butl errno = 0; if (struct dirent* de = readdir (h_)) { - const char* n (de->d_name); + // We can accept some overhead for '.' and '..' (relying on short + // string optimization) in favor of a more compact code. + // + path p (de->d_name); // Skip '.' and '..'. // - if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) + if (p.current () || p.parent ()) continue; - e_.p_ = path (n); + e_.p_ = move (p); e_.t_ = d_type (de, nullptr); e_.lt_ = entry_type::unknown; } @@ -665,14 +668,17 @@ namespace butl if (r) { - const char* n (fi.name); + // We can accept some overhead for '.' and '..' (relying on short + // string optimization) in favor of a more compact code. + // + path p (fi.name); // Skip '.' and '..'. // - if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0'))) + if (p.current () || p.parent ()) continue; - e_.p_ = path (n); + e_.p_ = move (p); // We do not support symlinks at the moment. // diff --git a/butl/path b/butl/path index 2e243ff..726bbe6 100644 --- a/butl/path +++ b/butl/path @@ -121,6 +121,30 @@ namespace butl #endif } + static bool + current (const string_type& s) + { + return current (s.c_str (), s.size ()); + } + + static bool + current (const C* s, size_type n) + { + return n == 1 && s[0] == '.'; + } + + static bool + parent (const string_type& s) + { + return parent (s.c_str (), s.size ()); + } + + static bool + parent (const C* s, size_type n) + { + return n == 2 && s[0] == '.' && s[1] == '.'; + } + static size_type find_separator (string_type const& s, size_type pos = 0, @@ -276,16 +300,16 @@ namespace butl // the underlying OS errors. // static string_type - current (); + current_directory (); static void - current (string_type const&); + current_directory (string_type const&); // Return the user home directory. Throw std::system_error to report the // underlying OS errors. // static string_type - home (); + home_directory (); // Return the temporary directory. Throw std::system_error to report the // underlying OS errors. @@ -548,16 +572,16 @@ namespace butl // the underlying OS errors. // static dir_type - current () {return dir_type (traits::current ());} + current_directory () {return dir_type (traits::current_directory ());} static void - current (basic_path const&); + current_directory (basic_path const&); // Return the user home directory. Throw std::system_error to report the // underlying OS errors. // static dir_type - home () {return dir_type (traits::home ());} + home_directory () {return dir_type (traits::home_directory ());} // Return the temporary directory. Throw std::system_error to report the // underlying OS errors. @@ -603,6 +627,20 @@ namespace butl bool root () const; + // The following predicates return true for the "." and ".." paths, + // respectively. Note that the result doesn't depend on the presence or + // spelling of the trailing directory separator. + // + // Also note that the path must literally match the specified values rather + // than be semantically current or parent. For example for paths "foo/.." + // or "bar/../.." the predicates return false. + // + bool + current () const; + + bool + parent () const; + // Test, based on the presence/absence of the trailing separator, if the // path is to a directory. // diff --git a/butl/path.cxx b/butl/path.cxx index 03d4b1f..d77f9a4 100644 --- a/butl/path.cxx +++ b/butl/path.cxx @@ -60,7 +60,7 @@ namespace butl template <> LIBBUTL_EXPORT path_traits::string_type path_traits:: - current () + current_directory () { #ifdef _WIN32 char cwd[_MAX_PATH]; @@ -78,7 +78,7 @@ namespace butl template <> LIBBUTL_EXPORT void path_traits:: - current (string_type const& s) + current_directory (string_type const& s) { #ifdef _WIN32 if (_chdir (s.c_str ()) != 0) @@ -173,10 +173,10 @@ namespace butl template <> LIBBUTL_EXPORT path_traits::string_type path_traits:: - home () + home_directory () { #ifndef _WIN32 - return butl::home (); + return home (); #else // Could be set by, e.g., MSYS and Cygwin shells. // @@ -223,7 +223,7 @@ namespace butl template <> LIBBUTL_EXPORT path_traits::string_type path_traits:: - current () + current_directory () { #ifdef _WIN32 wchar_t wcwd[_MAX_PATH]; @@ -245,7 +245,7 @@ namespace butl template <> LIBBUTL_EXPORT void path_traits:: - current (string_type const& s) + current_directory (string_type const& s) { #ifdef _WIN32 if (_wchdir (s.c_str ()) != 0) @@ -305,11 +305,11 @@ namespace butl template <> LIBBUTL_EXPORT path_traits::string_type path_traits:: - home () + home_directory () { #ifndef _WIN32 wchar_t d[PATH_MAX]; - size_t r (mbstowcs (d, butl::home ().c_str (), PATH_MAX)); + size_t r (mbstowcs (d, home ().c_str (), PATH_MAX)); if (r == size_t (-1)) throw system_error (EINVAL, system_category ()); diff --git a/butl/path.ixx b/butl/path.ixx index c87a376..b1212f4 100644 --- a/butl/path.ixx +++ b/butl/path.ixx @@ -88,6 +88,20 @@ namespace butl template inline bool basic_path:: + current () const + { + return traits::current (this->path_); + } + + template + inline bool basic_path:: + parent () const + { + return traits::parent (this->path_); + } + + template + inline bool basic_path:: root () const { const string_type& s (this->path_); @@ -232,7 +246,7 @@ namespace butl complete () { if (relative ()) - *this = current () / *this; + *this = current_directory () / *this; return *this; } diff --git a/butl/path.txx b/butl/path.txx index 0cdaf5b..7c46dbe 100644 --- a/butl/path.txx +++ b/butl/path.txx @@ -193,10 +193,7 @@ namespace butl if (!tsep) { const string_type& l (ps.back ()); - size_type ln (l.size ()); - - if ((ln == 1 && l[0] == '.') || - (ln == 2 && l[0] == '.' && l[1] == '.')) + if (traits::current (l) || traits::parent (l)) tsep = true; } } @@ -208,30 +205,21 @@ namespace butl for (typename paths::iterator i (ps.begin ()), e (ps.end ()); i != e; ++i) { string_type& s (*i); - size_type n (s.size ()); - if (n == 1 && s[0] == '.') + if (traits::current (s)) continue; - if (n == 2 && s[0] == '.' && s[1] == '.') + // If '..' then pop the last directory from r unless it is '..'. + // + if (traits::parent (s) && !r.empty () && !traits::parent (r.back ())) { - // Pop the last directory from r unless it is '..'. + // Cannot go past the root directory. // - if (!r.empty ()) - { - string_type const& s1 (r.back ()); + if (abs && r.size () == 1) + throw invalid_basic_path (this->path_); - if (!(s1.size () == 2 && s1[0] == '.' && s1[1] == '.')) - { - // Cannot go past the root directory. - // - if (abs && r.size () == 1) - throw invalid_basic_path (this->path_); - - r.pop_back (); - continue; - } - } + r.pop_back (); + continue; } r.push_back (std::move (s)); @@ -286,7 +274,7 @@ namespace butl } else if (!cur_empty) // Collapse to canonical current directory. { - p = "."; + p.assign (1, '.'); ts = 1; // Canonical separator is always first. } else // Collapse to empty path. @@ -304,14 +292,14 @@ namespace butl template void basic_path:: - current (basic_path const& p) + current_directory (basic_path const& p) { const string_type& s (p.string ()); if (s.empty ()) throw invalid_basic_path (s); - traits::current (s); + traits::current_directory (s); } template diff --git a/butl/process.cxx b/butl/process.cxx index cf4b26d..df9fb1f 100644 --- a/butl/process.cxx +++ b/butl/process.cxx @@ -189,7 +189,7 @@ namespace butl } else { - const string& d (traits::current ()); + const string& d (traits::current_directory ()); if (search (d.c_str (), d.size (), true)) return r; @@ -507,7 +507,7 @@ namespace butl } else { - const string& d (traits::current ()); + const string& d (traits::current_directory ()); if (search (d.c_str (), d.size (), true)) // Appends extension. return r; @@ -567,7 +567,7 @@ namespace butl // idea to prepend .\ for clarity. // { - const string& d (traits::current ()); + const string& d (traits::current_directory ()); if (search (d.c_str (), d.size ())) return r; @@ -582,8 +582,8 @@ namespace butl e = strchr (b, traits::path_separator); // Empty path (i.e., a double colon or a colon at the beginning or end - // of PATH) means search in the current dirrectory. Silently skip - // invalid paths. + // of PATH) means search in the current directory. Silently skip invalid + // paths. // try { diff --git a/tests/path/driver.cxx b/tests/path/driver.cxx index fe192ce..4806cea 100644 --- a/tests/path/driver.cxx +++ b/tests/path/driver.cxx @@ -141,6 +141,15 @@ main () assert (path ("C:\\foo.txt\\").base ().representation () == "C:\\foo\\"); #endif + // current/parent + // + assert (path (".").current ()); + assert (path ("./").current ()); + assert (!path (".abc").current ()); + assert (path ("..").parent ()); + assert (path ("../").parent ()); + assert (!path ("..abc").parent ()); + // iteration // { @@ -483,8 +492,8 @@ main () assert (path::temp_directory ().absolute ()); //assert (wpath::temp_directory ().absolute ()); - assert (path::home ().absolute ()); - //assert (wpath::home ().absolute ()); + assert (path::home_directory ().absolute ()); + //assert (wpath::home_directory ().absolute ()); // normalize and actualize // @@ -503,7 +512,7 @@ main () assert (test ("c:\\pROGRAM fILES/NonSense\\sTUFF/") == "C:\\Program Files\\NonSense\\sTUFF\\"); - dir_path cwd (path::current ()); + dir_path cwd (path::current_directory ()); assert (cwd.normalize (true).representation () == cwd.representation ()); } #endif @@ -512,7 +521,7 @@ main () path p ("../foo"); p.complete (); - cerr << path::current () << endl; + cerr << path::current_directory () << endl; cerr << p << endl; p.normalize (); cerr << p << endl; diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx index bc75cc2..ac8f54d 100644 --- a/tests/process/driver.cxx +++ b/tests/process/driver.cxx @@ -225,7 +225,7 @@ main (int argc, const char* argv[]) // write it to cout and/or cerr. // - if (!wd.empty () && wd.realize () != dir_path::current ()) + if (!wd.empty () && wd.realize () != dir_path::current_directory ()) return 1; try @@ -305,7 +305,7 @@ main (int argc, const char* argv[]) // Execute the child using the relative path. // - dir_path::current (fp.directory ()); + dir_path::current_directory (fp.directory ()); assert (exec (dir_path (".") / fp.leaf ())); @@ -326,7 +326,7 @@ main (int argc, const char* argv[]) assert (_putenv (("PATH=" + paths).c_str ()) == 0); #endif - dir_path::current (fp.directory () / dir_path ("..")); + dir_path::current_directory (fp.directory () / dir_path ("..")); assert (exec (fp.leaf ())); -- cgit v1.1