aboutsummaryrefslogtreecommitdiff
path: root/butl/path.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'butl/path.cxx')
-rw-r--r--butl/path.cxx107
1 files changed, 102 insertions, 5 deletions
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>::