From b2733572c98ce85457b3820afe8aa2f72614b858 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 13 Jun 2016 19:33:15 +0300 Subject: Add path::home() --- butl/path | 36 +++++++++++++------- butl/path.cxx | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- butl/path.txx | 11 ++++-- 3 files changed, 134 insertions(+), 20 deletions(-) (limited to 'butl') diff --git a/butl/path b/butl/path index 472e421..193eae4 100644 --- a/butl/path +++ b/butl/path @@ -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 friend P butl::path_cast (basic_path&&); - // 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 #ifdef _WIN32 -# include // _MAX_PATH +# include // _MAX_PATH, _wgetenv() # include // _[w]getcwd(), _[w]chdir() # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif -# include // GetTempPath*() -# include // unique_ptr +# include // GetTempPath*(), FormatMessageA(), LocalFree() +# include // SHGetFolderPath*(), CSIDL_PROFILE +# include // SUCCEEDED() +# include // unique_ptr #else +# include // struct passwd, getpwuid_r() # include // EINVAL # include // mbstowcs(), wcstombs(), realpath(), getenv() # include // PATH_MAX @@ -20,6 +23,7 @@ # include // strlen(), strcpy() # include // stat(), S_IS* # include // stat +# include #endif #include @@ -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 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::string_type path_traits:: + 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:: @@ -270,6 +333,40 @@ namespace butl L"-" + to_wstring (temp_name_count++); } + template <> + path_traits::string_type path_traits:: + 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:: 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 (s); + { + if (exact) + return false; + else + throw invalid_basic_path (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) ; -- cgit v1.1