aboutsummaryrefslogtreecommitdiff
path: root/butl
diff options
context:
space:
mode:
Diffstat (limited to 'butl')
-rw-r--r--butl/path32
-rw-r--r--butl/path.cxx147
2 files changed, 173 insertions, 6 deletions
diff --git a/butl/path b/butl/path
index 07b05ca..df76f99 100644
--- a/butl/path
+++ b/butl/path
@@ -159,6 +159,20 @@ namespace butl
static void
current (string_type const&);
+ // Return the temporary directory. Throw std::system_error to report the
+ // underlying OS errors.
+ //
+ static string_type
+ temp_directory ();
+
+ // Return a temporary name. The name is constructed by starting with the
+ // prefix followed by the process id following by a unique counter value
+ // inside the process. Throw std::system_error to report the underlying OS
+ // errors.
+ //
+ static string_type
+ temp_name (string_type const& prefix);
+
// Make the path real (by calling realpath(3)). Throw invalid_basic_path
// if the path is invalid (e.g., some components do not exist) and
// std::system_error to report other underlying OS errors.
@@ -302,6 +316,24 @@ namespace butl
static void
current (basic_path const&);
+ // Return the temporary directory. Throw std::system_error to report the
+ // underlying OS errors.
+ //
+ static dir_type
+ temp_directory () {return dir_type (traits::temp_directory ());}
+
+ // Return a temporary path. The path is constructed by starting with the
+ // temporary directory and then appending a path component consisting of
+ // the prefix followed by the process id following by a unique counter
+ // value inside the process. Throw std::system_error to report the
+ // underlying OS errors.
+ //
+ static basic_path
+ temp_path (const string_type& prefix)
+ {
+ return temp_directory () / basic_path (traits::temp_name (prefix));
+ }
+
public:
bool
empty () const
diff --git a/butl/path.cxx b/butl/path.cxx
index 3e82190..1f8f195 100644
--- a/butl/path.cxx
+++ b/butl/path.cxx
@@ -5,18 +5,29 @@
#include <butl/path>
#ifdef _WIN32
-# include <stdlib.h> // _MAX_PATH
-# include <direct.h> // _[w]getcwd(), _[w]chdir()
+# include <stdlib.h> // _MAX_PATH
+# 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
#else
-# include <errno.h> // EINVAL
-# include <stdlib.h> // mbstowcs(), wcstombs(), realpath()
-# include <limits.h> // PATH_MAX
-# include <unistd.h> // getcwd(), chdir()
+# include <errno.h> // EINVAL
+# include <stdlib.h> // mbstowcs(), wcstombs(), realpath(), getenv()
+# include <limits.h> // PATH_MAX
+# include <unistd.h> // getcwd(), chdir()
+# include <string.h> // strlen(), strcpy()
+# include <sys/stat.h> // stat(), S_IS*
+# include <sys/types.h> // stat
#endif
+#include <atomic>
#include <cassert>
#include <system_error>
+#include <butl/process>
+
#ifndef _WIN32
# ifndef PATH_MAX
# define PATH_MAX 4096
@@ -69,6 +80,88 @@ namespace butl
#endif
}
+#ifdef _WIN32
+ struct msg_deleter
+ {
+ void operator() (char* p) const {LocalFree (p);}
+ };
+
+ static string
+ last_error ()
+ {
+ DWORD e (GetLastError ());
+ char* msg;
+ if (!FormatMessageA (
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ 0,
+ e,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (char*)&msg,
+ 0,
+ 0))
+ return "unknown error code " + to_string (e);
+
+ unique_ptr<char, msg_deleter> m (msg);
+ return msg;
+ }
+#else
+ static const char*
+ temp_directory ()
+ {
+ const char* dir (nullptr);
+ const char* env[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr};
+
+ for (auto e (env); dir == nullptr && *e != nullptr; ++e)
+ dir = getenv (*e);
+
+ if (dir == nullptr)
+ dir = "/tmp";
+
+ struct stat s;
+ if (stat (dir, &s) != 0)
+ throw system_error (errno, system_category ());
+
+ if (!S_ISDIR (s.st_mode))
+ throw system_error (ENOTDIR, system_category ());
+
+ return dir;
+ }
+#endif
+
+ template <>
+ path_traits<char>::string_type path_traits<char>::
+ temp_directory ()
+ {
+#ifdef _WIN32
+ char d[_MAX_PATH + 1];
+ DWORD r (GetTempPathA (_MAX_PATH + 1, d));
+
+ if (r == 0)
+ {
+ string e (last_error ());
+ throw system_error (ENOTDIR, system_category (), e);
+ }
+
+ return string_type (d);
+#else
+ return string_type (butl::temp_directory ());
+#endif
+ }
+
+ static atomic<size_t> temp_name_count;
+
+ template <>
+ path_traits<char>::string_type path_traits<char>::
+ temp_name (string_type const& prefix)
+ {
+ return prefix
+ + "-" + to_string (process::current_id ())
+ + "-" + to_string (temp_name_count++);
+ }
+
#ifndef _WIN32
template <>
void path_traits<char>::
@@ -135,6 +228,48 @@ namespace butl
#endif
}
+ template <>
+ path_traits<wchar_t>::string_type path_traits<wchar_t>::
+ temp_directory ()
+ {
+#ifdef _WIN32
+ wchar_t d[_MAX_PATH + 1];
+ DWORD r (GetTempPathW (_MAX_PATH + 1, d));
+
+ if (r == 0)
+ {
+ string e (last_error ());
+ throw system_error (ENOTDIR, system_category (), e);
+ }
+#else
+ wchar_t d[PATH_MAX];
+
+ // The usage of mbstowcs() supposes the program's C-locale is set to the
+ // proper locale before the call (can be done with setlocale(LC_ALL, "...")
+ // call). Otherwise mbstowcs() fails with EILSEQ errno for non-ASCII
+ // directory paths.
+ //
+ size_t r (mbstowcs (d, butl::temp_directory (), PATH_MAX));
+
+ if (r == size_t (-1))
+ throw system_error (EINVAL, system_category ());
+
+ if (r == PATH_MAX)
+ throw system_error (ENOTSUP, system_category ());
+#endif
+
+ return string_type (d);
+ }
+
+ template <>
+ path_traits<wchar_t>::string_type path_traits<wchar_t>::
+ temp_name (string_type const& prefix)
+ {
+ return prefix +
+ L"-" + to_wstring (process::current_id ()) +
+ L"-" + to_wstring (temp_name_count++);
+ }
+
#ifndef _WIN32
template <>
void path_traits<wchar_t>::