summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2018-01-10 18:25:26 +1100
committer Vas Crabb <vas@vastheman.com>2018-01-10 18:25:26 +1100
commitd697e8a992268228243278cc2b5a8f46b07a9208 (patch)
tree4c09eba64058c7d0cc3a78cb7b7b4c6278a3e9a2 /src/lib/util
parent9ee066992dd0126b7ed7450d664bf055d527ab64 (diff)
Convert zippath directory to a C++ interface
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/zippath.cpp1252
-rw-r--r--src/lib/util/zippath.h42
2 files changed, 555 insertions, 739 deletions
diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp
index f85e5c63b71..e04281cd98e 100644
--- a/src/lib/util/zippath.cpp
+++ b/src/lib/util/zippath.cpp
@@ -22,63 +22,8 @@
namespace util {
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-
-/**
- * @class zippath_directory
- *
- * @brief A zippath directory.
- */
-
-class zippath_directory
-{
-public:
- zippath_directory()
- : returned_parent(false)
- , directory()
- , called_zip_first(false)
- , zipfile()
- , returned_dirlist()
- {
- }
-
- /* common */
- /** @brief true to returned parent. */
- bool returned_parent;
- /** @brief The returned entry. */
- osd::directory::entry returned_entry;
-
- /* specific to normal directories */
- /** @brief Pathname of the directory. */
- osd::directory::ptr directory;
-
- /* specific to ZIP directories */
- /** @brief true to called zip first. */
- bool called_zip_first;
- /** @brief The zipfile. */
- archive_file::ptr zipfile;
- /** @brief The zipprefix. */
- std::string zipprefix;
- /** @brief The returned dirlist. */
- std::forward_list<std::string> returned_dirlist;
-};
-
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
-
-static int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type);
-static bool is_zip_file(std::string const &path);
-static bool is_zip_file_separator(char c);
-static bool is_7z_file(std::string const &path);
-
-
-/***************************************************************************
- PATH OPERATIONS
-***************************************************************************/
+namespace {
/**
* @fn int is_path_separator(char c)
@@ -94,424 +39,25 @@ static bool is_7z_file(std::string const &path);
int is_path_separator(char c)
{
+ // FIXME: don't assume DOS-like behaviour - backslashes are valid filename characters on *NIX
return (c == '/') || (c == '\\');
}
// -------------------------------------------------
-// parse_parent_path - parses out the parent path
-// -------------------------------------------------
-
-/**
- * @fn static void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos)
- *
- * @brief Parse parent path.
- *
- * @param path Full pathname of the file.
- * @param [in,out] beginpos If non-null, the beginpos.
- * @param [in,out] endpos If non-null, the endpos.
- */
-
-static void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos)
-{
- std::string::size_type length = path.length();
- std::string::size_type pos;
-
- // skip over trailing path separators
- pos = length ? (length - 1) : std::string::npos;
- while ((pos > 0) && (pos != std::string::npos) && is_path_separator(path[pos]))
- pos--;
-
- // return endpos
- if (endpos != nullptr)
- *endpos = pos;
-
- // now skip until we find a path separator
- while ((pos != std::string::npos) && !is_path_separator(path[pos]))
- pos = (pos > 0) ? pos - 1 : std::string::npos;
-
- // return beginpos
- if (beginpos != nullptr)
- *beginpos = pos;
-}
-
-
-
-// -------------------------------------------------
-// zippath_parent - retrieves the parent directory
-// -------------------------------------------------
-
-std::string &zippath_parent(std::string &dst, const std::string &path)
-{
- std::string::size_type pos;
- parse_parent_path(path, &pos, nullptr);
-
- if (pos != std::string::npos)
- dst = path.substr(0, pos + 1);
- else
- dst.clear();
- return dst;
-}
-
-
-
-// -------------------------------------------------
-// zippath_parent - retrieves the parent directory
-// -------------------------------------------------
-
-std::string zippath_parent(const std::string &path)
-{
- std::string result;
- zippath_parent(result, path);
- return result;
-}
-
-
-
-// -------------------------------------------------
-// zippath_parent_basename - retrieves the parent
-// directory basename
-// -------------------------------------------------
-
-/**
- * @fn std::string &zippath_parent_basename(std::string &dst, const std::string &path)
- *
- * @brief Zippath parent basename.
- *
- * @param [in,out] dst Destination for the.
- * @param path Full pathname of the file.
- *
- * @return A std::string&amp;
- */
-
-std::string &zippath_parent_basename(std::string &dst, const std::string &path)
-{
- std::string::size_type beginpos, endpos;
- parse_parent_path(path, &beginpos, &endpos);
- dst.copy((char*)(path.c_str() + beginpos + 1), endpos - beginpos);
- return dst;
-}
-
-
-
-// -------------------------------------------------
-// zippath_parent_basename - retrieves the parent
-// directory basename
-// -------------------------------------------------
-
-std::string zippath_parent_basename(const std::string &path)
-{
- std::string result;
- zippath_parent_basename(result, path);
- return result;
-}
-
-
-
-// -------------------------------------------------
-// zippath_combine - combines two paths
-// -------------------------------------------------
-
-/**
- * @fn std::string &zippath_combine(std::string &dst, const char *path1, const char *path2)
- *
- * @brief Zippath combine.
- *
- * @param [in,out] dst Destination for the.
- * @param path1 The first path.
- * @param path2 The second path.
- *
- * @return A std::string&amp;
- */
-
-std::string &zippath_combine(std::string &dst, const std::string &path1, const std::string &path2)
-{
- if (path2 == ".")
- {
- dst.assign(path1);
- }
- else if (path2 == "..")
- {
- dst = zippath_parent(path1);
- }
- else if (osd_is_absolute_path(path2))
- {
- dst.assign(path2);
- }
- else if (!path1.empty() && !is_path_separator(*path1.rbegin()))
- {
- dst.assign(path1).append(PATH_SEPARATOR).append(path2);
- }
- else
- {
- dst.assign(path1).append(path2);
- }
- return dst;
-}
-
-
-
-// -------------------------------------------------
-// zippath_combine - combines two paths
-// -------------------------------------------------
-
-std::string zippath_combine(const std::string &path1, const std::string &path2)
-{
- std::string result;
- zippath_combine(result, path1, path2);
- return result;
-}
-
-
-
-/***************************************************************************
- FILE OPERATIONS
-***************************************************************************/
-
-// -------------------------------------------------
-// file_error_from_zip_error - translates a
-// osd_file::error to a zip_error
-// -------------------------------------------------
-
-/**
- * @fn static osd_file::error file_error_from_zip_error(archive_file::error ziperr)
- *
- * @brief File error from zip error.
- *
- * @param ziperr The ziperr.
- *
- * @return A osd_file::error.
- */
-
-static osd_file::error file_error_from_zip_error(archive_file::error ziperr)
-{
- osd_file::error filerr;
- switch(ziperr)
- {
- case archive_file::error::NONE:
- filerr = osd_file::error::NONE;
- break;
- case archive_file::error::OUT_OF_MEMORY:
- filerr = osd_file::error::OUT_OF_MEMORY;
- break;
- case archive_file::error::BAD_SIGNATURE:
- case archive_file::error::DECOMPRESS_ERROR:
- case archive_file::error::FILE_TRUNCATED:
- case archive_file::error::FILE_CORRUPT:
- case archive_file::error::UNSUPPORTED:
- case archive_file::error::FILE_ERROR:
- filerr = osd_file::error::INVALID_DATA;
- break;
- case archive_file::error::BUFFER_TOO_SMALL:
- default:
- filerr = osd_file::error::FAILURE;
- break;
- }
- return filerr;
-}
-
-
-// -------------------------------------------------
-// create_core_file_from_zip - creates a core_file
-// from a zip file entry
-// -------------------------------------------------
-
-/**
- * @fn static osd_file::error create_core_file_from_zip(archive_file *zip, util::core_file::ptr &file)
- *
- * @brief Creates core file from zip.
- *
- * @param [in,out] zip If non-null, the zip.
- * @param header The header.
- * @param [in,out] file [in,out] If non-null, the file.
- *
- * @return The new core file from zip.
- */
-
-static osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
-{
- osd_file::error filerr;
- archive_file::error ziperr;
- void *ptr;
-
- ptr = malloc(zip.current_uncompressed_length());
- if (ptr == nullptr)
- {
- filerr = osd_file::error::OUT_OF_MEMORY;
- goto done;
- }
-
- ziperr = zip.decompress(ptr, zip.current_uncompressed_length());
- if (ziperr != archive_file::error::NONE)
- {
- filerr = file_error_from_zip_error(ziperr);
- goto done;
- }
-
- filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file);
- if (filerr != osd_file::error::NONE)
- goto done;
-
-done:
- if (ptr != nullptr)
- free(ptr);
- return filerr;
-}
-
-
-// -------------------------------------------------
-// zippath_fopen - opens a zip path file
-// -------------------------------------------------
-
-/**
- * @fn osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
- *
- * @brief Zippath fopen.
- *
- * @param filename Filename of the file.
- * @param openflags The openflags.
- * @param [in,out] file [in,out] If non-null, the file.
- * @param [in,out] revised_path Full pathname of the revised file.
- *
- * @return A osd_file::error.
- */
-
-osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
-{
- osd_file::error filerr = osd_file::error::NOT_FOUND;
- archive_file::error ziperr;
- archive_file::ptr zip;
- int header;
- osd::directory::entry::entry_type entry_type;
- int len;
-
- /* first, set up the two types of paths */
- std::string mainpath(filename);
- std::string subpath;
- file = nullptr;
-
- /* loop through */
- while((file == nullptr) && (mainpath.length() > 0)
- && ((openflags == OPEN_FLAG_READ) || (subpath.length() == 0)))
- {
- /* is the mainpath a ZIP path? */
- if (is_zip_file(mainpath) || is_7z_file(mainpath))
- {
- /* this file might be a zip file - lets take a look */
- ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip);
- if (ziperr == archive_file::error::NONE)
- {
- /* it is a zip file - error if we're not opening for reading */
- if (openflags != OPEN_FLAG_READ)
- {
- filerr = osd_file::error::ACCESS_DENIED;
- goto done;
- }
-
- if (subpath.length() > 0)
- header = zippath_find_sub_path(*zip, subpath, entry_type);
- else
- header = zip->first_file();
-
- if (header < 0)
- {
- filerr = osd_file::error::NOT_FOUND;
- goto done;
- }
-
- /* attempt to read the file */
- filerr = create_core_file_from_zip(*zip, file);
- if (filerr != osd_file::error::NONE)
- goto done;
-
- /* update subpath, if appropriate */
- if (subpath.length() == 0)
- subpath.assign(zip->current_name());
-
- /* we're done */
- goto done;
- }
- }
-
- if (subpath.length() == 0)
- filerr = util::core_file::open(filename, openflags, file);
- else
- filerr = osd_file::error::NOT_FOUND;
-
- /* if we errored, then go up a directory */
- if (filerr != osd_file::error::NONE)
- {
- /* go up a directory */
- auto temp = zippath_parent(mainpath);
-
- /* append to the sub path */
- if (subpath.length() > 0)
- {
- std::string temp2;
- mainpath = mainpath.substr(temp.length());
- temp2.assign(mainpath).append(PATH_SEPARATOR).append(subpath);
- subpath.assign(temp2);
- }
- else
- {
- mainpath = mainpath.substr(temp.length());
- subpath.assign(mainpath);
- }
- /* get the new main path, truncating path separators */
- len = temp.length();
- while (len > 0 && is_zip_file_separator(temp[len - 1]))
- len--;
- temp = temp.substr(0, len);
- mainpath.assign(temp);
- }
- }
-
-done:
- /* store the revised path */
- revised_path.clear();
- if (filerr == osd_file::error::NONE)
- {
- /* cannonicalize mainpath */
- std::string alloc_fullpath;
- filerr = osd_get_full_path(alloc_fullpath, mainpath);
- if (filerr == osd_file::error::NONE)
- {
- revised_path = alloc_fullpath;
- if (subpath.length() > 0)
- revised_path.append(PATH_SEPARATOR).append(subpath);
- }
- }
-
- return filerr;
-}
-
-
-/***************************************************************************
- DIRECTORY OPERATIONS
-***************************************************************************/
-
-// -------------------------------------------------
// is_root - tests to see if this path is the root
// -------------------------------------------------
-/**
- * @fn static int is_root(const char *path)
- *
- * @brief Is root.
- *
- * @param path Full pathname of the file.
- *
- * @return An int.
- */
-
-static int is_root(const char *path)
+bool is_root(std::string const &path)
{
- int i = 0;
+ // FIXME: get rid of the assumption that paths are DOS-like
+
+ std::string::size_type i = 0;
- /* skip drive letter */
+ // skip drive letter
if (isalpha(path[i]) && (path[i + 1] == ':'))
i += 2;
- /* skip path separators */
+ // skip path separators
while (is_path_separator(path[i]))
i++;
@@ -525,17 +71,7 @@ static int is_root(const char *path)
// 7-zip file
// -------------------------------------------------
-/**
- * @fn static int is_7z_file(const char *path)
- *
- * @brief Is 7z file.
- *
- * @param path Full pathname of the file.
- *
- * @return An int.
- */
-
-static bool is_7z_file(std::string const &path)
+bool is_7z_file(std::string const &path)
{
auto const s = path.rfind('.');
return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".7z");
@@ -547,7 +83,7 @@ static bool is_7z_file(std::string const &path)
// ZIP file
// -------------------------------------------------
-static bool is_zip_file(std::string const &path)
+bool is_zip_file(std::string const &path)
{
auto const s = path.rfind('.');
return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".zip");
@@ -560,17 +96,7 @@ static bool is_zip_file(std::string const &path)
// character is a path separator within a ZIP file
// -------------------------------------------------
-/**
- * @fn static int is_zip_file_separator(char c)
- *
- * @brief Is zip file separator.
- *
- * @param c The character.
- *
- * @return An int.
- */
-
-static bool is_zip_file_separator(char c)
+bool is_zip_file_separator(char c)
{
return (c == '/') || (c == '\\');
}
@@ -582,17 +108,7 @@ static bool is_zip_file_separator(char c)
// character is a path separator within a ZIP path
// -------------------------------------------------
-/**
- * @fn static int is_zip_path_separator(char c)
- *
- * @brief Is zip path separator.
- *
- * @param c The character.
- *
- * @return An int.
- */
-
-static bool is_zip_path_separator(char c)
+bool is_zip_path_separator(char c)
{
return is_zip_file_separator(c) || is_path_separator(c);
}
@@ -604,18 +120,7 @@ static bool is_zip_path_separator(char c)
// character, normalizing separators as '/'
// -------------------------------------------------
-/**
- * @fn static char next_path_char(const char *s, int *pos)
- *
- * @brief Next path character.
- *
- * @param s The const char * to process.
- * @param [in,out] pos If non-null, the position.
- *
- * @return A char.
- */
-
-static char next_path_char(std::string const &s, std::string::size_type &pos)
+char next_path_char(std::string const &s, std::string::size_type &pos)
{
// skip over any initial separators
if (pos == 0)
@@ -654,19 +159,7 @@ static char next_path_char(std::string const &s, std::string::size_type &pos)
// type of a sub path in a zip file
// -------------------------------------------------
-/**
- * @fn static const zip_file_header *zippath_find_sub_path(archive_file *zipfile, const char *subpath, osd::directory::entry::entry_type *type)
- *
- * @brief Zippath find sub path.
- *
- * @param [in,out] zipfile If non-null, the zipfile.
- * @param subpath The subpath.
- * @param [in,out] type If non-null, the type.
- *
- * @return null if it fails, else a zip_file_header*.
- */
-
-static int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type)
+int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type)
{
for (int header = zipfile.first_file(); header >= 0; header = zipfile.next_file())
{
@@ -701,24 +194,39 @@ static int zippath_find_sub_path(archive_file &zipfile, std::string const &subpa
// -------------------------------------------------
+// parse_parent_path - parses out the parent path
+// -------------------------------------------------
+
+void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos)
+{
+ std::string::size_type length = path.length();
+ std::string::size_type pos;
+
+ // skip over trailing path separators
+ pos = length ? (length - 1) : std::string::npos;
+ while ((pos > 0) && (pos != std::string::npos) && is_path_separator(path[pos]))
+ pos--;
+
+ // return endpos
+ if (endpos != nullptr)
+ *endpos = pos;
+
+ // now skip until we find a path separator
+ while ((pos != std::string::npos) && !is_path_separator(path[pos]))
+ pos = (pos > 0) ? pos - 1 : std::string::npos;
+
+ // return beginpos
+ if (beginpos != nullptr)
+ *beginpos = pos;
+}
+
+
+// -------------------------------------------------
// zippath_resolve - separates a ZIP path out into
// true path and ZIP entry components
// -------------------------------------------------
-/**
- * @fn static osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file *&zipfile, std::string &newpath)
- *
- * @brief Zippath resolve.
- *
- * @param path Full pathname of the file.
- * @param [in,out] entry_type Type of the entry.
- * @param [in,out] zipfile [in,out] If non-null, the zipfile.
- * @param [in,out] newpath The newpath.
- *
- * @return A osd_file::error.
- */
-
-static osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
+osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
{
newpath.clear();
@@ -793,278 +301,584 @@ static osd_file::error zippath_resolve(const char *path, osd::directory::entry::
// -------------------------------------------------
-// zippath_opendir - opens a directory
+// file_error_from_zip_error - translates a
+// osd_file::error to a zip_error
// -------------------------------------------------
-/**
- * @fn osd_file::error zippath_opendir(const std::string &path, zippath_directory **directory)
- *
- * @brief Zippath opendir.
- *
- * @param path Full pathname of the file.
- * @param [in,out] directory If non-null, pathname of the directory.
- *
- * @return A osd_file::error.
- */
+osd_file::error file_error_from_zip_error(archive_file::error ziperr)
+{
+ osd_file::error filerr;
+ switch(ziperr)
+ {
+ case archive_file::error::NONE:
+ filerr = osd_file::error::NONE;
+ break;
+ case archive_file::error::OUT_OF_MEMORY:
+ filerr = osd_file::error::OUT_OF_MEMORY;
+ break;
+ case archive_file::error::BAD_SIGNATURE:
+ case archive_file::error::DECOMPRESS_ERROR:
+ case archive_file::error::FILE_TRUNCATED:
+ case archive_file::error::FILE_CORRUPT:
+ case archive_file::error::UNSUPPORTED:
+ case archive_file::error::FILE_ERROR:
+ filerr = osd_file::error::INVALID_DATA;
+ break;
+ case archive_file::error::BUFFER_TOO_SMALL:
+ default:
+ filerr = osd_file::error::FAILURE;
+ break;
+ }
+ return filerr;
+}
+
-osd_file::error zippath_opendir(const std::string &path, zippath_directory **directory)
+// -------------------------------------------------
+// create_core_file_from_zip - creates a core_file
+// from a zip file entry
+// -------------------------------------------------
+
+osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
{
- osd_file::error err;
+ osd_file::error filerr;
+ archive_file::error ziperr;
- /* allocate a directory */
- zippath_directory *result = nullptr;
- try
+ void *ptr = malloc(zip.current_uncompressed_length());
+ if (!ptr)
{
- result = new zippath_directory;
+ filerr = osd_file::error::OUT_OF_MEMORY;
+ goto done;
}
- catch (std::bad_alloc &)
+
+ ziperr = zip.decompress(ptr, zip.current_uncompressed_length());
+ if (ziperr != archive_file::error::NONE)
{
- err = osd_file::error::OUT_OF_MEMORY;
+ filerr = file_error_from_zip_error(ziperr);
goto done;
}
- /* resolve the path */
- osd::directory::entry::entry_type entry_type;
- err = zippath_resolve(path.c_str(), entry_type, result->zipfile, result->zipprefix);
- if (err != osd_file::error::NONE)
+
+ filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file);
+ if (filerr != osd_file::error::NONE)
goto done;
- /* we have to be a directory */
- if (entry_type != osd::directory::entry::entry_type::DIR)
+done:
+ if (ptr != nullptr)
+ free(ptr);
+ return filerr;
+}
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class zippath_directory_base : public zippath_directory
+{
+protected:
+ zippath_directory_base(bool fake_parent) : m_returned_parent(!fake_parent)
{
- err = osd_file::error::NOT_FOUND;
- goto done;
}
- /* was the result a ZIP? */
- if (!result->zipfile)
+ virtual osd::directory::entry const *readdir() override
{
- /* a conventional directory */
- result->directory = osd::directory::open(path);
- if (!result->directory)
+ if (!m_returned_parent)
+ {
+ // return a fake entry representing the parent before anything else
+ m_returned_parent = true;
+ m_synthetic_entry.name = "..";
+ m_synthetic_entry.type = osd::directory::entry::entry_type::DIR;
+ m_synthetic_entry.size = 0; // FIXME: what would stat say?
+ // FIXME: modified time?
+ return &m_synthetic_entry;
+ }
+ else
{
- err = osd_file::error::FAILURE;
- goto done;
+ return read_excluding_parent();
}
+ }
+
+ osd::directory::entry m_synthetic_entry;
+
+private:
+ virtual osd::directory::entry const *read_excluding_parent() = 0;
+
+ bool m_returned_parent;
+};
+
- /* is this path the root? if so, pretend we've already returned the parent */
- if (is_root(path.c_str()))
- result->returned_parent = true;
+class archive_directory_impl : public zippath_directory_base
+{
+public:
+ archive_directory_impl(archive_file::ptr &&zipfile, std::string &&zipprefix) :
+ zippath_directory_base(true),
+ m_zipfile(std::move(zipfile)),
+ m_zipprefix(std::move(zipprefix))
+ {
}
-done:
- if ((directory == nullptr || err != osd_file::error::NONE) && result != nullptr)
+ virtual bool is_archive() const override { return true; }
+
+private:
+ virtual osd::directory::entry const *read_excluding_parent() override
+ {
+ osd::directory::entry const *result = nullptr;
+ char const *relpath = nullptr;
+ do
+ {
+ // a zip file read
+ int header;
+ do
+ {
+ header = m_called_zip_first ? m_zipfile->next_file() : m_zipfile->first_file();
+ m_called_zip_first = true;
+ relpath = nullptr;
+ }
+ while ((header >= 0) && ((relpath = get_relative_path()) == nullptr));
+
+ if (relpath)
+ {
+ // we've found a ZIP entry; but this may be an entry deep within the target directory
+ char const *separator = relpath;
+ while (*separator && !is_zip_file_separator(*separator)) separator++;
+
+ if (*separator || m_zipfile->current_is_directory())
+ {
+ // a nested entry; loop through returned_dirlist to see if we've returned the parent directory
+ auto const len(separator - relpath);
+ auto rdent = m_returned_dirlist.begin();
+ while (m_returned_dirlist.end() != rdent)
+ {
+ if ((rdent->length() == len) && !core_strnicmp(rdent->c_str(), relpath, len))
+ break;
+ else
+ ++rdent;
+ }
+
+ if (m_returned_dirlist.end() == rdent)
+ {
+ // we've found a new directory; add this to returned_dirlist
+ m_returned_dirlist.emplace_front(relpath, separator - relpath);
+
+ // ...and return it
+ m_synthetic_entry.name = m_returned_dirlist.front().c_str();
+ m_synthetic_entry.type = osd::directory::entry::entry_type::DIR;
+ m_synthetic_entry.size = 0; // FIXME: what would stat say?
+ // FIXME: modified time?
+ result = &m_synthetic_entry;
+ }
+ }
+ else
+ {
+ // a real file
+ m_synthetic_entry.name = relpath;
+ m_synthetic_entry.type = osd::directory::entry::entry_type::FILE;
+ m_synthetic_entry.size = m_zipfile->current_uncompressed_length();
+ m_synthetic_entry.last_modified = m_zipfile->current_last_modified();
+ result = &m_synthetic_entry;
+ }
+ }
+ }
+ while (relpath && !result);
+ return result;
+ }
+
+
+ // -------------------------------------------------
+ // get_relative_path - checks to see if a specified
+ // header is in the zippath_directory, and if so
+ // returns the relative path
+ // -------------------------------------------------
+
+ char const *get_relative_path() const
+ {
+ std::string::size_type len = m_zipprefix.length();
+ char const *prefix = m_zipprefix.c_str();
+ while (is_zip_file_separator(*prefix))
+ {
+ len--;
+ prefix++;
+ }
+
+ std::string const &current(m_zipfile->current_name());
+ char const *result = current.c_str() + len;
+ if ((current.length() >= len) &&
+ !strncmp(prefix, current.c_str(), len) &&
+ (!*prefix || is_zip_file_separator(*result) || is_zip_file_separator(m_zipprefix.back())))
+ {
+ while (is_zip_file_separator(*result))
+ result++;
+
+ return *result ? result : nullptr;
+ }
+ else
+ {
+ return nullptr;
+ }
+ }
+
+
+ bool m_called_zip_first = false;
+ archive_file::ptr const m_zipfile;
+ std::string const m_zipprefix;
+ std::forward_list<std::string> m_returned_dirlist;
+};
+
+
+class filesystem_diectory_impl : public zippath_directory_base
+{
+public:
+ filesystem_diectory_impl(std::string const &path) : zippath_directory_base(!is_root(path)), m_directory(osd::directory::open(path))
+ {
+ }
+
+ virtual bool is_archive() const override { return false; }
+
+ explicit operator bool() const { return bool(m_directory); }
+
+private:
+ virtual osd::directory::entry const *read_excluding_parent() override
+ {
+ assert(m_directory);
+
+ // a normal directory read
+ osd::directory::entry const *result = nullptr;
+ do
+ {
+ result = m_directory->read();
+ }
+ while (result && (!strcmp(result->name, ".") || !strcmp(result->name, "..")));
+
+ // special case - is this entry a ZIP file? if so we need to return it as a "directory"
+ if (result && (is_zip_file(result->name) || is_7z_file(result->name)))
+ {
+ // copy; but change the entry type
+ m_synthetic_entry = *result;
+ m_synthetic_entry.type = osd::directory::entry::entry_type::DIR;
+ m_synthetic_entry.size = 0; // FIXME: what would stat say?
+ return &m_synthetic_entry;
+ }
+ else
+ {
+ return result;
+ }
+ }
+
+ osd::directory::ptr const m_directory;
+};
+
+} // anonymous namespace
+
+
+// -------------------------------------------------
+// zippath_directory::open - opens a directory
+// -------------------------------------------------
+
+osd_file::error zippath_directory::open(std::string const &path, ptr &directory)
+{
+ try
+ {
+ // resolve the path
+ osd::directory::entry::entry_type entry_type;
+ archive_file::ptr zipfile;
+ std::string zipprefix;
+ osd_file::error const err = zippath_resolve(path.c_str(), entry_type, zipfile, zipprefix);
+ if (osd_file::error::NONE != err)
+ return err;
+
+ // we have to be a directory
+ if (osd::directory::entry::entry_type::DIR != entry_type)
+ return osd_file::error::NOT_FOUND;
+
+ // was the result a ZIP?
+ if (zipfile)
+ {
+ directory = std::make_unique<archive_directory_impl>(std::move(zipfile), std::move(zipprefix));
+ return osd_file::error::NONE;
+ }
+ else
+ {
+ // a conventional directory
+ std::unique_ptr<filesystem_diectory_impl> result(new filesystem_diectory_impl(path));
+ if (!*result)
+ return osd_file::error::FAILURE;
+
+ directory = std::move(result);
+ return osd_file::error::NONE;
+ }
+ }
+ catch (std::bad_alloc const &)
{
- zippath_closedir(result);
- result = nullptr;
+ return osd_file::error::OUT_OF_MEMORY;
}
- if (directory != nullptr)
- *directory = result;
- return err;
}
// -------------------------------------------------
-// zippath_closedir - closes a directory
+// zippath_directory::~zippath_directory - closes
+// a directory
+// -------------------------------------------------
+
+zippath_directory::~zippath_directory()
+{
+}
+
+
+// -------------------------------------------------
+// zippath_parent - retrieves the parent directory
+// -------------------------------------------------
+
+std::string &zippath_parent(std::string &dst, const std::string &path)
+{
+ std::string::size_type pos;
+ parse_parent_path(path, &pos, nullptr);
+
+ if (pos != std::string::npos)
+ dst = path.substr(0, pos + 1);
+ else
+ dst.clear();
+ return dst;
+}
+
+
+
+// -------------------------------------------------
+// zippath_parent - retrieves the parent directory
+// -------------------------------------------------
+
+std::string zippath_parent(const std::string &path)
+{
+ std::string result;
+ zippath_parent(result, path);
+ return result;
+}
+
+
+
+// -------------------------------------------------
+// zippath_parent_basename - retrieves the parent
+// directory basename
// -------------------------------------------------
/**
- * @fn void zippath_closedir(zippath_directory *directory)
+ * @fn std::string &zippath_parent_basename(std::string &dst, const std::string &path)
*
- * @brief Zippath closedir.
+ * @brief Zippath parent basename.
*
- * @param [in,out] directory If non-null, pathname of the directory.
+ * @param [in,out] dst Destination for the.
+ * @param path Full pathname of the file.
+ *
+ * @return A std::string&amp;
*/
-void zippath_closedir(zippath_directory *directory)
+std::string &zippath_parent_basename(std::string &dst, const std::string &path)
{
- if (directory->directory != nullptr)
- directory->directory.reset();
+ std::string::size_type beginpos, endpos;
+ parse_parent_path(path, &beginpos, &endpos);
+ dst.copy((char*)(path.c_str() + beginpos + 1), endpos - beginpos);
+ return dst;
+}
+
- if (directory->zipfile != nullptr)
- directory->zipfile.reset();
- directory->returned_dirlist.clear();
+// -------------------------------------------------
+// zippath_parent_basename - retrieves the parent
+// directory basename
+// -------------------------------------------------
- delete directory;
+std::string zippath_parent_basename(const std::string &path)
+{
+ std::string result;
+ zippath_parent_basename(result, path);
+ return result;
}
+
// -------------------------------------------------
-// get_relative_path - checks to see if a specified
-// header is in the zippath_directory, and if so
-// returns the relative path
+// zippath_combine - combines two paths
// -------------------------------------------------
/**
- * @fn static const char *get_relative_path(zippath_directory *directory, const zip_file_header *header)
+ * @fn std::string &zippath_combine(std::string &dst, const char *path1, const char *path2)
*
- * @brief Gets relative path.
+ * @brief Zippath combine.
*
- * @param [in,out] directory If non-null, pathname of the directory.
- * @param header The header.
+ * @param [in,out] dst Destination for the.
+ * @param path1 The first path.
+ * @param path2 The second path.
*
- * @return null if it fails, else the relative path.
+ * @return A std::string&amp;
*/
-static const char *get_relative_path(zippath_directory const &directory)
+std::string &zippath_combine(std::string &dst, const std::string &path1, const std::string &path2)
{
- auto len = directory.zipprefix.length();
- char const *prefix = directory.zipprefix.c_str();
- while (is_zip_file_separator(*prefix))
+ if (path2 == ".")
{
- len--;
- prefix++;
+ dst.assign(path1);
}
-
- std::string const &current(directory.zipfile->current_name());
- char const *result = directory.zipfile->current_name().c_str() + len;
- if ((current.length() >= len) &&
- !strncmp(prefix, current.c_str(), len) &&
- (!*prefix || is_zip_file_separator(*result) || is_zip_file_separator(directory.zipprefix.back())))
+ else if (path2 == "..")
{
- while (is_zip_file_separator(*result))
- result++;
-
- return *result ? result : nullptr;
+ dst = zippath_parent(path1);
+ }
+ else if (osd_is_absolute_path(path2))
+ {
+ dst.assign(path2);
+ }
+ else if (!path1.empty() && !is_path_separator(*path1.rbegin()))
+ {
+ dst.assign(path1).append(PATH_SEPARATOR).append(path2);
}
else
{
- return nullptr;
+ dst.assign(path1).append(path2);
}
+ return dst;
+}
+
+
+
+// -------------------------------------------------
+// zippath_combine - combines two paths
+// -------------------------------------------------
+
+std::string zippath_combine(const std::string &path1, const std::string &path2)
+{
+ std::string result;
+ zippath_combine(result, path1, path2);
+ return result;
}
+
+/***************************************************************************
+ FILE OPERATIONS
+***************************************************************************/
+
// -------------------------------------------------
-// zippath_readdir - reads a directory
+// zippath_fopen - opens a zip path file
// -------------------------------------------------
/**
- * @fn const osd::directory::entry *zippath_readdir(zippath_directory *directory)
+ * @fn osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
*
- * @brief Zippath readdir.
+ * @brief Zippath fopen.
*
- * @param [in,out] directory If non-null, pathname of the directory.
+ * @param filename Filename of the file.
+ * @param openflags The openflags.
+ * @param [in,out] file [in,out] If non-null, the file.
+ * @param [in,out] revised_path Full pathname of the revised file.
*
- * @return null if it fails, else an osd::directory::entry*.
+ * @return A osd_file::error.
*/
-const osd::directory::entry *zippath_readdir(zippath_directory *directory)
+osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
{
- const osd::directory::entry *result = nullptr;
+ osd_file::error filerr = osd_file::error::NOT_FOUND;
+ archive_file::error ziperr;
+ archive_file::ptr zip;
+ int header;
+ osd::directory::entry::entry_type entry_type;
+ int len;
- if (!directory->returned_parent)
- {
- /* first thing's first - return parent directory */
- directory->returned_parent = true;
- directory->returned_entry.name = "..";
- directory->returned_entry.type = osd::directory::entry::entry_type::DIR;
- directory->returned_entry.size = 0; // FIXME: what would stat say?
- // FIXME: modified time?
- result = &directory->returned_entry;
- }
- else if (directory->directory)
- {
- /* a normal directory read */
- do
- {
- result = directory->directory->read();
- }
- while (result && (!strcmp(result->name, ".") || !strcmp(result->name, "..")));
+ /* first, set up the two types of paths */
+ std::string mainpath(filename);
+ std::string subpath;
+ file = nullptr;
- /* special case - is this entry a ZIP file? if so we need to return it as a "directory" */
- if (result && (is_zip_file(result->name) || is_7z_file(result->name)))
- {
- /* copy; but change the entry type */
- directory->returned_entry = *result;
- directory->returned_entry.type = osd::directory::entry::entry_type::DIR;
- directory->returned_entry.size = 0; // FIXME: what would stat say?
- // FIXME: modified time?
- result = &directory->returned_entry;
- }
- }
- else if (directory->zipfile)
+ /* loop through */
+ while((file == nullptr) && (mainpath.length() > 0)
+ && ((openflags == OPEN_FLAG_READ) || (subpath.length() == 0)))
{
- char const *relpath;
- do
+ /* is the mainpath a ZIP path? */
+ if (is_zip_file(mainpath) || is_7z_file(mainpath))
{
- /* a zip file read */
- int header;
- do
- {
- header = directory->called_zip_first ? directory->zipfile->next_file() : directory->zipfile->first_file();
- directory->called_zip_first = true;
- relpath = nullptr;
- }
- while ((header >= 0) && ((relpath = get_relative_path(*directory)) == nullptr));
-
- if (relpath)
+ /* this file might be a zip file - lets take a look */
+ ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip);
+ if (ziperr == archive_file::error::NONE)
{
- /* we've found a ZIP entry; but this may be an entry deep within the target directory */
- char const *separator = relpath;
- while (*separator && !is_zip_file_separator(*separator)) separator++;
-
- if (*separator || directory->zipfile->current_is_directory())
+ /* it is a zip file - error if we're not opening for reading */
+ if (openflags != OPEN_FLAG_READ)
{
- /* a nested entry; loop through returned_dirlist to see if we've returned the parent directory */
- auto const len(separator - relpath);
- auto rdent = directory->returned_dirlist.begin();
- while (directory->returned_dirlist.end() != rdent)
- {
- if ((rdent->length() == len) && !core_strnicmp(rdent->c_str(), relpath, len))
- break;
- else
- ++rdent;
- }
-
- if (directory->returned_dirlist.end() == rdent)
- {
- /* we've found a new directory; add this to returned_dirlist */
- directory->returned_dirlist.emplace_front(relpath, separator - relpath);
-
- /* ...and return it */
- directory->returned_entry.name = directory->returned_dirlist.front().c_str();
- directory->returned_entry.type = osd::directory::entry::entry_type::DIR;
- directory->returned_entry.size = 0; // FIXME: what would stat say?
- // FIXME: modified time?
- result = &directory->returned_entry;
- }
+ filerr = osd_file::error::ACCESS_DENIED;
+ goto done;
}
+
+ if (subpath.length() > 0)
+ header = zippath_find_sub_path(*zip, subpath, entry_type);
else
+ header = zip->first_file();
+
+ if (header < 0)
{
- /* a real file */
- directory->returned_entry.name = relpath;
- directory->returned_entry.type = osd::directory::entry::entry_type::FILE;
- directory->returned_entry.size = directory->zipfile->current_uncompressed_length();
- directory->returned_entry.last_modified = directory->zipfile->current_last_modified();
- result = &directory->returned_entry;
+ filerr = osd_file::error::NOT_FOUND;
+ goto done;
}
+
+ /* attempt to read the file */
+ filerr = create_core_file_from_zip(*zip, file);
+ if (filerr != osd_file::error::NONE)
+ goto done;
+
+ /* update subpath, if appropriate */
+ if (subpath.length() == 0)
+ subpath.assign(zip->current_name());
+
+ /* we're done */
+ goto done;
}
}
- while (relpath && !result);
- }
- return result;
-}
+ if (subpath.length() == 0)
+ filerr = util::core_file::open(filename, openflags, file);
+ else
+ filerr = osd_file::error::NOT_FOUND;
+ /* if we errored, then go up a directory */
+ if (filerr != osd_file::error::NONE)
+ {
+ /* go up a directory */
+ auto temp = zippath_parent(mainpath);
-// -------------------------------------------------
-// zippath_is_zip - returns true if this path is
-// a ZIP path or false if not
-// -------------------------------------------------
+ /* append to the sub path */
+ if (subpath.length() > 0)
+ {
+ std::string temp2;
+ mainpath = mainpath.substr(temp.length());
+ temp2.assign(mainpath).append(PATH_SEPARATOR).append(subpath);
+ subpath.assign(temp2);
+ }
+ else
+ {
+ mainpath = mainpath.substr(temp.length());
+ subpath.assign(mainpath);
+ }
+ /* get the new main path, truncating path separators */
+ len = temp.length();
+ while (len > 0 && is_zip_file_separator(temp[len - 1]))
+ len--;
+ temp = temp.substr(0, len);
+ mainpath.assign(temp);
+ }
+ }
-/**
- * @fn int zippath_is_zip(zippath_directory *directory)
- *
- * @brief Zippath is zip.
- *
- * @param [in,out] directory If non-null, pathname of the directory.
- *
- * @return An int.
- */
+done:
+ /* store the revised path */
+ revised_path.clear();
+ if (filerr == osd_file::error::NONE)
+ {
+ /* cannonicalize mainpath */
+ std::string alloc_fullpath;
+ filerr = osd_get_full_path(alloc_fullpath, mainpath);
+ if (filerr == osd_file::error::NONE)
+ {
+ revised_path = alloc_fullpath;
+ if (subpath.length() > 0)
+ revised_path.append(PATH_SEPARATOR).append(subpath);
+ }
+ }
-bool zippath_is_zip(zippath_directory *directory)
-{
- return directory->zipfile != nullptr;
+ return filerr;
}
} // namespace util
diff --git a/src/lib/util/zippath.h b/src/lib/util/zippath.h
index 3bef7fc9d39..cf6c2249f9c 100644
--- a/src/lib/util/zippath.h
+++ b/src/lib/util/zippath.h
@@ -8,22 +8,40 @@
***************************************************************************/
-#pragma once
-
#ifndef MAME_LIB_UTIL_ZIPPATH_H
#define MAME_LIB_UTIL_ZIPPATH_H
+#pragma once
+
#include "corefile.h"
-#include <string>
#include "unzip.h"
+#include <string>
+
namespace util {
+
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
-class zippath_directory;
+class zippath_directory
+{
+public:
+ typedef std::unique_ptr<zippath_directory> ptr;
+
+ // opens a directory
+ static osd_file::error open(std::string const &path, ptr &directory);
+
+ // closes a directory
+ virtual ~zippath_directory();
+
+ // reads a directory entry
+ virtual osd::directory::entry const *readdir() = 0;
+
+ // returns true if this directory is an archive or false if it is a filesystem directory
+ virtual bool is_archive() const = 0;
+};
@@ -51,22 +69,6 @@ std::string zippath_combine(const std::string &path1, const std::string &path2);
// opens a zip path file
osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path);
-
-// ----- directory operations ----- */
-
-// opens a directory
-osd_file::error zippath_opendir(const std::string &path, zippath_directory **directory);
-
-// closes a directory
-void zippath_closedir(zippath_directory *directory);
-
-// reads a directory entry
-const osd::directory::entry *zippath_readdir(zippath_directory *directory);
-
-// returns true if this path is a ZIP path or false if not
-bool zippath_is_zip(zippath_directory *directory);
-
} // namespace util
-
#endif // MAME_LIB_UTIL_ZIPPATH_H