diff options
Diffstat (limited to 'src/osd/modules/file/posixdir.cpp')
-rw-r--r-- | src/osd/modules/file/posixdir.cpp | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/src/osd/modules/file/posixdir.cpp b/src/osd/modules/file/posixdir.cpp new file mode 100644 index 00000000000..d08d6869c6c --- /dev/null +++ b/src/osd/modules/file/posixdir.cpp @@ -0,0 +1,293 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert, R. Belmont, Vas Crabb +//============================================================ +// +// sdldir.c - SDL core directory access functions +// +// SDLMAME by Olivier Galibert and R. Belmont +// +//============================================================ + + +#ifndef _LARGEFILE64_SOURCE +#define _LARGEFILE64_SOURCE +#endif + +#ifdef __linux__ +#ifndef __USE_LARGEFILE64 +#define __USE_LARGEFILE64 +#endif +#ifndef __USE_BSD +#define __USE_BSD +#endif +#endif + +#ifdef WIN32 +#define _FILE_OFFSET_BITS 64 +#endif + +#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__bsdi__) && !defined(__DragonFly__) +#ifdef _XOPEN_SOURCE +#if _XOPEN_SOURCE < 500 +#undef _XOPEN_SOURCE +#endif +#endif +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 500 +#endif +#endif + +#undef _POSIX_C_SOURCE // to get DT_xxx on OS X + + +#include "osdcore.h" +#include "modules/lib/osdlib.h" +#include "util/strformat.h" + +#include <cctype> +#include <cstdlib> +#include <cstring> +#include <memory> +#include <string> +#include <utility> + +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> + + + +namespace osd { +namespace { +//============================================================ +// CONSTANTS +//============================================================ + +#if defined(WIN32) +constexpr char PATHSEPCH = '\\'; +constexpr char INVPATHSEPCH = '/'; +#else +constexpr char PATHSEPCH = '/'; +constexpr char INVPATHSEPCH = '\\'; +#endif + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) || defined(__ANDROID__) || defined(WIN32) || defined(SDLMAME_NO64BITIO) +using sdl_dirent = struct dirent; +using sdl_stat = struct stat; +#define sdl_readdir readdir +#define sdl_stat_fn stat +#else +using sdl_dirent = struct dirent64; +using sdl_stat = struct stat64; +#define sdl_readdir readdir64 +#define sdl_stat_fn stat64 +#endif + +#define HAS_DT_XXX (defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)) + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +class posix_directory : public osd::directory +{ +public: + posix_directory(); + virtual ~posix_directory() override; + + virtual const entry *read() override; + + bool open(std::string const &dirname); + +private: + typedef std::unique_ptr<DIR, int (*)(DIR *)> dir_ptr; + + entry m_entry; + sdl_dirent *m_data; + dir_ptr m_fd; + std::string m_path; +}; + + +//============================================================ +// posix_directory::~posix_directory +//============================================================ + +posix_directory::posix_directory() + : m_entry() + , m_data(nullptr) + , m_fd(nullptr, &::closedir) + , m_path() +{ +} + + +//============================================================ +// posix_directory::~posix_directory +//============================================================ + +posix_directory::~posix_directory() +{ +} + + +//============================================================ +// posix_directory::read +//============================================================ + +const osd::directory::entry *posix_directory::read() +{ + m_data = sdl_readdir(m_fd.get()); + if (!m_data) + return nullptr; + + m_entry.name = m_data->d_name; + + sdl_stat st; + bool stat_err(0 > sdl_stat_fn(util::string_format("%s%c%s", m_path, PATHSEPCH, m_data->d_name).c_str(), &st)); + +#if HAS_DT_XXX + switch (m_data->d_type) + { + case DT_DIR: + m_entry.type = entry::entry_type::DIR; + break; + case DT_REG: + m_entry.type = entry::entry_type::FILE; + break; + case DT_LNK: + if (stat_err) + m_entry.type = entry::entry_type::OTHER; + else if (S_ISDIR(st.st_mode)) + m_entry.type = entry::entry_type::DIR; + else + m_entry.type = entry::entry_type::FILE; + break; + default: + m_entry.type = entry::entry_type::OTHER; + } +#else + if (stat_err) + m_entry.type = entry::entry_type::OTHER; + else if (S_ISDIR(st.st_mode)) + m_entry.type = entry::entry_type::DIR; + else + m_entry.type = entry::entry_type::FILE; +#endif + m_entry.size = stat_err ? 0 : std::uint64_t(std::make_unsigned_t<decltype(st.st_size)>(st.st_size)); + m_entry.last_modified = std::chrono::system_clock::from_time_t(st.st_mtime); + return &m_entry; +} + + +//============================================================ +// posix_directory::open +//============================================================ + +bool posix_directory::open(std::string const &dirname) +{ + assert(!m_fd); + + osd_subst_env(m_path, dirname); + m_fd.reset(::opendir(m_path.c_str())); + return bool(m_fd); +} + +} // anonymous namespace + + +//============================================================ +// osd::directory::open +//============================================================ + +directory::ptr directory::open(std::string const &dirname) +{ + ptr dir; + try { dir.reset(new posix_directory); } + catch (...) { return nullptr; } + + if (!dir->open(dirname)) + return nullptr; + + return dir; +} + +} // namespace osd + + +//============================================================ +// osd_subst_env +//============================================================ + +void osd_subst_env(std::string &dst, std::string const &src) +{ + std::string result, var; + auto start = src.begin(); + + // a leading tilde expands as $HOME + if ((src.end() != start) && ('~' == *start)) + { + char const *const home = std::getenv("HOME"); + if (home) + { + ++start; + if ((src.end() == start) || (osd::PATHSEPCH == *start)) + result.append(home); + else + result.push_back('~'); + } + } + + while (src.end() != start) + { + // find $ marking start of environment variable or end of string + auto it = start; + while ((src.end() != it) && ('$' != *it)) ++it; + if (start != it) result.append(start, it); + start = it; + + if (src.end() != start) + { + start = ++it; + if ((src.end() != start) && ('{' == *start)) + { + start = ++it; + for (++it; (src.end() != it) && ('}' != *it); ++it) { } + if (src.end() == it) + { + result.append("${").append(start, it); + start = it; + } + else + { + var.assign(start, it); + start = ++it; + const char *const exp = std::getenv(var.c_str()); + if (exp) + result.append(exp); + else + fprintf(stderr, "Warning: osd_subst_env variable %s not found.\n", var.c_str()); + } + } + else if ((src.end() != start) && (('_' == *start) || std::isalnum(*start))) + { + for (++it; (src.end() != it) && (('_' == *it) || std::isalnum(*it)); ++it) { } + var.assign(start, it); + start = it; + const char *const exp = std::getenv(var.c_str()); + if (exp) + result.append(exp); + else + fprintf(stderr, "Warning: osd_subst_env variable %s not found.\n", var.c_str()); + } + else + { + result.push_back('$'); + } + } + } + + dst = std::move(result); +} |