From abfdb61bc9c3281de2e5ef26df30634ed2c84d89 Mon Sep 17 00:00:00 2001 From: Jeroen Vermeulen Date: Sat, 18 Apr 2015 00:21:18 +0700 Subject: Cross-platform tempfile implementation. This makes temp_file and temp_dir work both on POSIX-like platforms and on Windows. It also fixes a bug where the temporary files/directories were created in the current working directory, instead of in the system's standard location for temporary files. Unfortunately the Windows and POSIX code diverge quite a bit on that point. --- util/tempfile.hh | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 3 deletions(-) (limited to 'util') diff --git a/util/tempfile.hh b/util/tempfile.hh index 228238823..9b872a27e 100644 --- a/util/tempfile.hh +++ b/util/tempfile.hh @@ -3,18 +3,74 @@ // Utilities for creating temporary files and directories. +#include #include #include #include +#if defined(_WIN32) || defined(_WIN64) +#include +#endif + #include #include #include "util/exception.hh" +#include "util/unistd.hh" namespace util { +/// Obtain a directory for temporary files, e.g. /tmp. +std::string temp_location() +{ +#if defined(_WIN32) || defined(_WIN64) + char dir_buffer[1000]; + if (GetTempPath(1000, dir_buffer) == 0) + throw std::runtime_error("Could not read temporary directory."); + return std::string(dir_buffer); +#else + // POSIX says to try these environment variables, in this order: + const char *const vars[] = {"TMPDIR", "TMP", "TEMPDIR", "TEMP", 0}; + for (int i=0; vars[i]; ++i) + { + const char *val = getenv(vars[i]); + // Environment variable is set and nonempty. Use it. + if (val && *val) return val; + } + // No environment variables set. Default to /tmp. + return "/tmp"; +#endif +} + + +#if defined(_WIN32) || defined(_WIN64) +/// Windows helper: create temporary filename. +std::string windows_tmpnam() +{ + const std::string tmp = temp_location(); + char output_buffer[MAX_PATH]; + if (GetTempFileName(tmp.c_str(), "tmp", 0, output_buffer) == 0) + throw std::runtime_error("Could not create temporary file name."); + return output_buffer; +} +#else +/** POSIX helper: create template for temporary filename. + * + * Writes the template into buf, which must have room for at least PATH_MAX + * bytes. The function fails if the template is too long. + */ +void posix_tmp_template(char *buf) +{ + const std::string tmp = temp_location(); + const std::string name_template = tmp + "/tmp.XXXXXX"; + if (name_template.size() >= PATH_MAX-1) + throw std::runtime_error("Path for temp files is too long: " + tmp); + strcpy(buf, name_template.c_str()); +} +#endif + + /** Temporary directory. * * Automatically creates, and on destruction deletes, a temporary directory. @@ -22,15 +78,21 @@ namespace util * object exists. * * If the directory no longer exists by the time the temp_dir is destroyed, - * no cleanup happens. + * cleanup is skipped. */ class temp_dir : boost::noncopyable { public: temp_dir() { - char buf[] = "tmpdir.XXXXXX"; +#if defined(_WIN32) || defined(_WIN64) + m_path = windows_tmpnam(); + boost::filesystem::create_directory(m_path); +#else + char buf[PATH_MAX]; + posix_tmp_template(buf); m_path = std::string(mkdtemp(buf)); +#endif } ~temp_dir() @@ -49,17 +111,27 @@ private: /** Temporary file. * * Automatically creates, and on destruction deletes, a temporary file. + * + * If the file no longer exists by the time the temp_file is destroyed, + * cleanup is skipped. */ class temp_file : boost::noncopyable { public: temp_file() { - char buf[] = "tmp.XXXXXX"; +#if defined(_WIN32) || defined(_WIN64) + m_path = windows_tmpnam(); + std::ofstream out(m_path.c_str()); + out.flush(); +#else + char buf[PATH_MAX]; + posix_tmp_template(buf); const int fd = mkstemp(buf); if (fd == -1) throw ErrnoException(); close(fd); m_path = buf; +#endif } ~temp_file() -- cgit v1.2.3