aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--butl/path36
-rw-r--r--butl/path.cxx107
-rw-r--r--butl/path.txx11
-rw-r--r--tests/path/driver.cxx8
4 files changed, 141 insertions, 21 deletions
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 <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 ();