diff options
-rw-r--r-- | butl/path | 36 | ||||
-rw-r--r-- | butl/path.cxx | 107 | ||||
-rw-r--r-- | butl/path.txx | 11 | ||||
-rw-r--r-- | tests/path/driver.cxx | 8 |
4 files changed, 141 insertions, 21 deletions
@@ -167,6 +167,12 @@ namespace butl static void current (string_type const&); + // Return the user home directory. Throw std::system_error to report the + // underlying OS errors. + // + static string_type + home (); + // Return the temporary directory. Throw std::system_error to report the // underlying OS errors. // @@ -273,9 +279,9 @@ namespace butl // basic_path () {}; - // Constructors that take a path string as an argument throw - // invalid_basic_path if the string is not a valid path (e.g. uses - // unsupported notation on Windows). + // Constructors that initialize a path from a string argument throw the + // invalid_path exception if the string is not a valid path (e.g., uses + // unsupported path notations on Windows). // explicit basic_path (C const* s): base_type (s) {init (this->path_);} @@ -292,11 +298,11 @@ namespace butl basic_path (const string_type& s, size_type p, size_type n) : base_type (string_type (s, p, n)) {init (this->path_);} - // Create a path using the exact string representation. If - // the string is not a valid path or if it would require a - // modification, then empty path is created instead and the - // passed string rvalue-reference is left untouched. See - // also string() && below. + // Create a path using the exact string representation. If the string is + // not a valid path or if it would require a modification, then empty path + // is created instead and the passed string rvalue-reference is left + // untouched. Note that no exception is thrown if the path is invalid. See + // also string()&& below. // enum exact_type {exact}; basic_path (string_type&& s, exact_type) @@ -328,6 +334,12 @@ namespace butl static void current (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 ());} + // Return the temporary directory. Throw std::system_error to report the // underlying OS errors. // @@ -617,10 +629,10 @@ namespace butl template <class P, class C1, class K1> friend P butl::path_cast (basic_path<C1, K1>&&); - // If exact is true, return whether the initialization was - // successful, that is, the passed string is a valid path - // and no modifications were necessary. Throw invalid_basic_path - // if the string is not a valid path (e.g. uses unsupported notation on + // If exact is true, return whether the initialization was successful, + // that is, the passed string is a valid path and no modifications were + // necessary. Otherwise (extact is false), throw invalid_path if the + // string is not a valid path (e.g., uses an unsupported path notation on // Windows). // bool diff --git a/butl/path.cxx b/butl/path.cxx index 0ec71d1..92b0d6c 100644 --- a/butl/path.cxx +++ b/butl/path.cxx @@ -5,14 +5,17 @@ #include <butl/path> #ifdef _WIN32 -# include <stdlib.h> // _MAX_PATH +# include <stdlib.h> // _MAX_PATH, _wgetenv() # include <direct.h> // _[w]getcwd(), _[w]chdir() # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include <windows.h> // GetTempPath*() -# include <memory> // unique_ptr +# include <windows.h> // GetTempPath*(), FormatMessageA(), LocalFree() +# include <shlobj.h> // SHGetFolderPath*(), CSIDL_PROFILE +# include <winerror.h> // SUCCEEDED() +# include <memory> // unique_ptr #else +# include <pwd.h> // struct passwd, getpwuid_r() # include <errno.h> // EINVAL # include <stdlib.h> // mbstowcs(), wcstombs(), realpath(), getenv() # include <limits.h> // PATH_MAX @@ -20,6 +23,7 @@ # include <string.h> // strlen(), strcpy() # include <sys/stat.h> // stat(), S_IS* # include <sys/types.h> // stat +# include <vector> #endif #include <atomic> @@ -87,9 +91,8 @@ namespace butl }; static string - last_error () + error_msg (DWORD e) { - DWORD e (GetLastError ()); char* msg; if (!FormatMessageA ( FORMAT_MESSAGE_ALLOCATE_BUFFER | @@ -107,6 +110,12 @@ namespace butl unique_ptr<char, msg_deleter> m (msg); return msg; } + + inline static string + last_error () + { + return error_msg (GetLastError ()); + } #else static const char* temp_directory () @@ -129,6 +138,35 @@ namespace butl return dir; } + + static string + home () + { + if (const char* h = getenv ("HOME")) + return h; + + // Struct passwd has 5 members that will use this buffer. Two are the + // home directory and shell paths. The other three are the user login + // name, password, and real name (comment). We expect them to fit into + // PATH_MAX * 2. + // + char buf[PATH_MAX * 4]; + + passwd pw; + passwd* rpw; + + int r (getpwuid_r (getuid (), &pw, buf, sizeof (buf), &rpw)); + if (r == -1) + throw system_error (errno, system_category ()); + + if (r == 0 && rpw == nullptr) + // According to POSIX errno should be left unchanged if an entry is not + // found. + // + throw system_error (ENOENT, system_category ()); + + return pw.pw_dir; + } #endif template <> @@ -162,6 +200,31 @@ namespace butl + "-" + to_string (temp_name_count++); } + template <> + path_traits<char>::string_type path_traits<char>:: + home () + { +#ifndef _WIN32 + return string_type (butl::home ()); +#else + // Could be set by, e.g., MSYS and Cygwin shells. + // + if (const char* h = getenv ("HOME")) + return string_type (h); + + char h[_MAX_PATH]; + HRESULT r (SHGetFolderPathA (NULL, CSIDL_PROFILE, NULL, 0, h)); + + if (!SUCCEEDED (r)) + { + string e (error_msg (r)); + throw system_error (ENOTDIR, system_category (), e); + } + + return string_type (h); +#endif + } + #ifndef _WIN32 template <> void path_traits<char>:: @@ -270,6 +333,40 @@ namespace butl L"-" + to_wstring (temp_name_count++); } + template <> + path_traits<wchar_t>::string_type path_traits<wchar_t>:: + home () + { +#ifndef _WIN32 + wchar_t d[PATH_MAX]; + size_t r (mbstowcs (d, butl::home ().c_str (), PATH_MAX)); + + if (r == size_t (-1)) + throw system_error (EINVAL, system_category ()); + + if (r == PATH_MAX) + throw system_error (ENOTSUP, system_category ()); + + return string_type (d); +#else + // Could be set by, e.g., MSYS and Cygwin shells. + // + if (const wchar_t* h = _wgetenv (L"HOME")) + return string_type (h); + + wchar_t h[_MAX_PATH]; + HRESULT r (SHGetFolderPathW (NULL, CSIDL_PROFILE, NULL, 0, h)); + + if (!SUCCEEDED (r)) + { + string e (error_msg (r)); + throw system_error (ENOTDIR, system_category (), e); + } + + return string_type (h); +#endif + } + #ifndef _WIN32 template <> void path_traits<wchar_t>:: diff --git a/butl/path.txx b/butl/path.txx index 383f7fe..35c6956 100644 --- a/butl/path.txx +++ b/butl/path.txx @@ -255,11 +255,16 @@ namespace butl // if ((n > 2 && s[1] == ':' && s[2] != '\\' && s[2] != '/') || (n > 1 && s[0] == '\\' && s[1] == '\\')) - throw invalid_basic_path<C> (s); + { + if (exact) + return false; + else + throw invalid_basic_path<C> (s); + } #endif - // Strip trailing slashes except for the case where the single - // slash represents the root directory. + // Strip trailing slashes except for the case where the single slash + // represents the root directory. // for (; n > 1 && traits::is_separator (s[n - 1]); --n) ; diff --git a/tests/path/driver.cxx b/tests/path/driver.cxx index f4b145a..ad76940 100644 --- a/tests/path/driver.cxx +++ b/tests/path/driver.cxx @@ -40,7 +40,7 @@ main () assert (path ("C:\\tmp\\foo\\").string () == "C:\\tmp\\foo"); #endif - // abslute/relative/root + // absolute/relative/root // #ifndef _WIN32 assert (path ("/").root ()); @@ -301,6 +301,12 @@ main () path ("foo/bar/baz")); #endif + assert (path::temp_directory ().absolute ()); + assert (wpath::temp_directory ().absolute ()); + + assert (path::home ().absolute ()); + assert (wpath::home ().absolute ()); + /* path p ("../foo"); p.complete (); |