summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/zippath.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/zippath.cpp')
-rw-r--r--src/lib/util/zippath.cpp349
1 files changed, 123 insertions, 226 deletions
diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp
index d8e90de11ff..a5bb2cae8af 100644
--- a/src/lib/util/zippath.cpp
+++ b/src/lib/util/zippath.cpp
@@ -9,14 +9,15 @@
***************************************************************************/
#include "zippath.h"
-#include "unzip.h"
-#include "corestr.h"
-#include "osdcore.h"
-#include <stdlib.h>
+#include "corestr.h"
+#include "path.h"
+#include "unzip.h"
+#include <algorithm>
#include <cassert>
#include <cctype>
+#include <cstdlib>
#include <forward_list>
#include <new>
@@ -47,21 +48,20 @@ int is_path_separator(char c)
// is_root - tests to see if this path is the root
// -------------------------------------------------
-bool is_root(std::string const &path)
+bool is_root(std::string_view path)
{
- // FIXME: get rid of the assumption that paths are DOS-like
-
- std::string::size_type i = 0;
+#if defined(_WIN32)
+ // FIXME: don't assume paths are DOS-like - UNC paths, \\?\ long path prefix, etc. complicate this
// skip drive letter
- if (isalpha(path[i]) && (path[i + 1] == ':'))
- i += 2;
+ if (path.length() >= 2 && isalpha(path[0]) && (path[1] == ':'))
+ path.remove_prefix(2);
// skip path separators
- while (is_path_separator(path[i]))
- i++;
-
- return path[i] == '\0';
+ return path.find_first_not_of(PATH_SEPARATOR) == std::string_view::npos;
+#else
+ return path.find_first_not_of(PATH_SEPARATOR) == std::string_view::npos;
+#endif
}
@@ -71,10 +71,9 @@ bool is_root(std::string const &path)
// 7-zip file
// -------------------------------------------------
-bool is_7z_file(std::string const &path)
+bool is_7z_file(std::string_view path)
{
- auto const s = path.rfind('.');
- return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".7z");
+ return core_filename_ends_with(path, ".7z");
}
@@ -83,10 +82,9 @@ bool is_7z_file(std::string const &path)
// ZIP file
// -------------------------------------------------
-bool is_zip_file(std::string const &path)
+bool is_zip_file(std::string_view path)
{
- auto const s = path.rfind('.');
- return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".zip");
+ return core_filename_ends_with(path, ".zip") || core_filename_ends_with(path, ".imz");
}
@@ -120,7 +118,7 @@ bool is_zip_path_separator(char c)
// character, normalizing separators as '/'
// -------------------------------------------------
-char next_path_char(std::string const &s, std::string::size_type &pos)
+char next_path_char(std::string_view s, std::string_view::size_type &pos)
{
// skip over any initial separators
if (pos == 0)
@@ -159,11 +157,11 @@ char next_path_char(std::string const &s, std::string::size_type &pos)
// type of a sub path in a zip file
// -------------------------------------------------
-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_view subpath, osd::directory::entry::entry_type &type)
{
for (int header = zipfile.first_file(); header >= 0; header = zipfile.next_file())
{
- std::string::size_type i = 0, j = 0;
+ std::string_view::size_type i = 0, j = 0;
char c1, c2;
do
{
@@ -194,57 +192,30 @@ int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd
// -------------------------------------------------
-// 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
// -------------------------------------------------
-osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
+std::error_condition zippath_resolve(std::string_view path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
{
newpath.clear();
// be conservative
- entry_type = osd::directory::entry::entry_type::NONE;
zipfile.reset();
std::string apath(path);
std::string apath_trimmed;
- osd::directory::entry::entry_type current_entry_type;
+ osd::directory::entry::entry_type current_entry_type = osd::directory::entry::entry_type::NONE;
bool went_up = false;
do
{
// trim the path of trailing path separators
- auto i = apath.length();
- while ((i > 1) && is_path_separator(apath[i - 1]))
- i--;
- apath.resize(i);
+ auto const i = apath.find_last_not_of(PATH_SEPARATOR);
+ if (i != std::string::npos)
+ apath.erase(std::max<decltype(i)>(i + 1, 2)); // don't erase drive letter
+ else if (!is_root(apath))
+ break;
+
apath_trimmed = apath;
// stat the path
@@ -259,77 +230,46 @@ osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_t
else
{
// if we have not found the file or directory, go up
- current_entry_type = osd::directory::entry::entry_type::NONE;
went_up = true;
apath = zippath_parent(apath);
}
}
- while ((current_entry_type == osd::directory::entry::entry_type::NONE) && !is_root(apath.c_str()));
+ while ((current_entry_type == osd::directory::entry::entry_type::NONE) && !is_root(apath));
// if we did not find anything, then error out
if (current_entry_type == osd::directory::entry::entry_type::NONE)
- return osd_file::error::NOT_FOUND;
+ {
+ entry_type = osd::directory::entry::entry_type::NONE;
+ return std::errc::no_such_file_or_directory;
+ }
// is this file a ZIP file?
if ((current_entry_type == osd::directory::entry::entry_type::FILE) &&
- ((is_zip_file(apath_trimmed) && (archive_file::open_zip(apath_trimmed, zipfile) == archive_file::error::NONE)) ||
- (is_7z_file(apath_trimmed) && (archive_file::open_7z(apath_trimmed, zipfile) == archive_file::error::NONE))))
+ ((is_zip_file(apath_trimmed) && !archive_file::open_zip(apath_trimmed, zipfile)) ||
+ (is_7z_file(apath_trimmed) && !archive_file::open_7z(apath_trimmed, zipfile))))
{
- auto i = strlen(path + apath.length());
+ auto i = path.length() - apath.length();
while ((i > 0) && is_zip_path_separator(path[apath.length() + i - 1]))
i--;
- newpath.assign(path + apath.length(), i);
+ newpath.assign(path, apath.length(), i);
// this was a true ZIP path - attempt to identify the type of path
zippath_find_sub_path(*zipfile, newpath, current_entry_type);
if (current_entry_type == osd::directory::entry::entry_type::NONE)
- return osd_file::error::NOT_FOUND;
+ return std::errc::no_such_file_or_directory;
}
else
{
// this was a normal path
if (went_up)
- return osd_file::error::NOT_FOUND;
+ return std::errc::no_such_file_or_directory;
newpath = path;
}
// success!
entry_type = current_entry_type;
- return osd_file::error::NONE;
-}
-
-
-// -------------------------------------------------
-// file_error_from_zip_error - translates a
-// osd_file::error to a zip_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;
+ return std::error_condition();
}
@@ -338,27 +278,24 @@ osd_file::error file_error_from_zip_error(archive_file::error ziperr)
// from a zip file entry
// -------------------------------------------------
-osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
+std::error_condition create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
{
- osd_file::error filerr;
- archive_file::error ziperr;
+ // TODO: would be more efficient if we could open a memory-based core_file with uninitialised contents and decompress into it
+ std::error_condition filerr;
void *ptr = malloc(zip.current_uncompressed_length());
if (!ptr)
{
- filerr = osd_file::error::OUT_OF_MEMORY;
+ filerr = std::errc::not_enough_memory;
goto done;
}
- ziperr = zip.decompress(ptr, zip.current_uncompressed_length());
- if (ziperr != archive_file::error::NONE)
- {
- filerr = file_error_from_zip_error(ziperr);
+ filerr = zip.decompress(ptr, zip.current_uncompressed_length());
+ if (filerr)
goto done;
- }
filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file);
- if (filerr != osd_file::error::NONE)
+ if (filerr)
goto done;
done:
@@ -415,6 +352,9 @@ public:
m_zipfile(std::move(zipfile)),
m_zipprefix(std::move(zipprefix))
{
+ for (char &ch : m_zipprefix)
+ if (is_path_separator(ch))
+ ch = '/';
}
virtual bool is_archive() const override { return true; }
@@ -520,15 +460,15 @@ private:
bool m_called_zip_first = false;
archive_file::ptr const m_zipfile;
- std::string const m_zipprefix;
+ std::string m_zipprefix;
std::forward_list<std::string> m_returned_dirlist;
};
-class filesystem_diectory_impl : public zippath_directory_base
+class filesystem_directory_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))
+ filesystem_directory_impl(std::string_view path) : zippath_directory_base(!is_root(path)), m_directory(osd::directory::open(std::string(path)))
{
}
@@ -574,7 +514,7 @@ private:
// zippath_directory::open - opens a directory
// -------------------------------------------------
-osd_file::error zippath_directory::open(std::string const &path, ptr &directory)
+std::error_condition zippath_directory::open(std::string_view path, ptr &directory)
{
try
{
@@ -582,34 +522,34 @@ osd_file::error zippath_directory::open(std::string const &path, ptr &directory)
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)
+ std::error_condition const err = zippath_resolve(path, entry_type, zipfile, zipprefix);
+ if (err)
return err;
// we have to be a directory
if (osd::directory::entry::entry_type::DIR != entry_type)
- return osd_file::error::NOT_FOUND;
+ return std::errc::not_a_directory;
// 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;
+ return std::error_condition();
}
else
{
// a conventional directory
- std::unique_ptr<filesystem_diectory_impl> result(new filesystem_diectory_impl(path));
+ std::unique_ptr<filesystem_directory_impl> result(new filesystem_directory_impl(path));
if (!*result)
- return osd_file::error::FAILURE;
+ return std::errc::io_error; // TODO: any way to give a better error here?
directory = std::move(result);
- return osd_file::error::NONE;
+ return std::error_condition();
}
}
catch (std::bad_alloc const &)
{
- return osd_file::error::OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
}
@@ -628,69 +568,18 @@ zippath_directory::~zippath_directory()
// zippath_parent - retrieves the parent directory
// -------------------------------------------------
-std::string &zippath_parent(std::string &dst, const std::string &path)
+std::string zippath_parent(std::string_view 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;
-}
-
-
+ // skip over trailing path separators
+ auto pos = std::find_if_not(path.rbegin(), path.rend(), &is_path_separator);
-// -------------------------------------------------
-// zippath_parent_basename - retrieves the parent
-// directory basename
-// -------------------------------------------------
+ // now skip until we find a path separator
+ pos = std::find_if(pos, path.rend(), &is_path_separator);
-std::string zippath_parent_basename(const std::string &path)
-{
- std::string result;
- zippath_parent_basename(result, path);
- return result;
+ if (path.rend() != pos)
+ return std::string(path.begin(), pos.base());
+ else
+ return std::string();
}
@@ -725,7 +614,7 @@ std::string &zippath_combine(std::string &dst, const std::string &path1, const s
{
dst.assign(path2);
}
- else if (!path1.empty() && !is_path_separator(*path1.rbegin()))
+ else if (!path1.empty() && !is_path_separator(path1.back()))
{
dst.assign(path1).append(PATH_SEPARATOR).append(path2);
}
@@ -760,7 +649,7 @@ std::string zippath_combine(const std::string &path1, const std::string &path2)
// -------------------------------------------------
/**
- * @fn osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
+ * @fn std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
*
* @brief Zippath fopen.
*
@@ -772,76 +661,84 @@ std::string zippath_combine(const std::string &path1, const std::string &path2)
* @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)
+std::error_condition zippath_fopen(std::string_view 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;
+ std::error_condition filerr = std::errc::no_such_file_or_directory;
archive_file::ptr zip;
- int header;
- osd::directory::entry::entry_type entry_type;
- int len;
- /* first, set up the two types of paths */
+ // 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)))
+ // loop through
+ while (!file && !mainpath.empty())
{
- /* is the mainpath a ZIP path? */
+ // 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)
+ // this file might be a zip file - lets take a look
+ std::error_condition const ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip);
+ if (!ziperr)
{
- /* it is a zip file - error if we're not opening for reading */
- if (openflags != OPEN_FLAG_READ)
+ osd::directory::entry::entry_type entry_type;
+ int header;
+ if (!subpath.empty())
{
- 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();
+ entry_type = osd::directory::entry::entry_type::FILE;
+ }
if (header < 0)
{
- filerr = osd_file::error::NOT_FOUND;
+ if (openflags & OPEN_FLAG_CREATE)
+ filerr = std::errc::permission_denied;
+ else
+ filerr = std::errc::no_such_file_or_directory;
+ goto done;
+ }
+ else if (osd::directory::entry::entry_type::DIR == entry_type)
+ {
+ filerr = std::errc::is_a_directory;
+ goto done;
+ }
+ else if (openflags & OPEN_FLAG_WRITE)
+ {
+ filerr = std::errc::permission_denied;
goto done;
}
- /* attempt to read the file */
+ // attempt to read the file
filerr = create_core_file_from_zip(*zip, file);
- if (filerr != osd_file::error::NONE)
+ if (filerr)
goto done;
- /* update subpath, if appropriate */
- if (subpath.length() == 0)
+ // update subpath, if appropriate
+ if (subpath.empty())
subpath.assign(zip->current_name());
- /* we're done */
+ // we're done
goto done;
}
}
- if (subpath.length() == 0)
+ if (subpath.empty())
filerr = util::core_file::open(filename, openflags, file);
else
- filerr = osd_file::error::NOT_FOUND;
+ filerr = std::errc::no_such_file_or_directory;
- /* if we errored, then go up a directory */
- if (filerr != osd_file::error::NONE)
+ // if we errored, then go up a directory
+ if (filerr)
{
- /* go up a directory */
+ // go up a directory
auto temp = zippath_parent(mainpath);
- /* append to the sub path */
- if (subpath.length() > 0)
+ // append to the sub path
+ if (!subpath.empty())
{
std::string temp2;
mainpath = mainpath.substr(temp.length());
@@ -853,8 +750,8 @@ osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, u
mainpath = mainpath.substr(temp.length());
subpath.assign(mainpath);
}
- /* get the new main path, truncating path separators */
- len = temp.length();
+ // get the new main path, truncating path separators
+ auto len = temp.length();
while (len > 0 && is_zip_file_separator(temp[len - 1]))
len--;
temp = temp.substr(0, len);
@@ -863,17 +760,17 @@ osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, u
}
done:
- /* store the revised path */
+ // store the revised path
revised_path.clear();
- if (filerr == osd_file::error::NONE)
+ if (!filerr)
{
- /* canonicalize mainpath */
+ // canonicalize mainpath
std::string alloc_fullpath;
filerr = osd_get_full_path(alloc_fullpath, mainpath);
- if (filerr == osd_file::error::NONE)
+ if (!filerr)
{
revised_path = alloc_fullpath;
- if (subpath.length() > 0)
+ if (!subpath.empty())
revised_path.append(PATH_SEPARATOR).append(subpath);
}
}