diff --git a/libs/pbd/file_archive.cc b/libs/pbd/file_archive.cc index 292c62dba9..68d5f0cebf 100644 --- a/libs/pbd/file_archive.cc +++ b/libs/pbd/file_archive.cc @@ -153,6 +153,8 @@ setup_archive () FileArchive::FileArchive (const std::string& url) : _req (url) + , _current_entry (0) + , _archive (0) { if (!_req.url) { fprintf (stderr, "Invalid Archive URL/filename\n"); @@ -166,6 +168,14 @@ FileArchive::FileArchive (const std::string& url) } } +FileArchive::~FileArchive () +{ + if (_archive) { + archive_read_close (_archive); + archive_read_free (_archive); + } +} + int FileArchive::inflate (const std::string& destdir) { @@ -197,6 +207,73 @@ FileArchive::contents () } } +std::string +FileArchive::next_file_name () +{ + assert (!_req.is_remote () && "FileArchive: Iterating over archive files not supported for remote archives.\n"); + + if (!_archive) { + _archive = setup_file_archive(); + if (!_archive) { + return std::string(); + } + } + + int r = archive_read_next_header (_archive, &_current_entry); + if (!_req.mp.progress) { + // file i/o -- not URL + const uint64_t read = archive_filter_bytes (_archive, -1); + progress (read, _req.mp.length); + } + + if (r == ARCHIVE_EOF) { + goto no_next; + } + + if (r != ARCHIVE_OK) { + fprintf (stderr, "Error reading archive: %s\n", archive_error_string(_archive)); + goto no_next; + } + + return archive_entry_pathname (_current_entry); + +no_next: + _current_entry = 0; + return std::string(); +} + +int +FileArchive::extract_current_file (const std::string& destpath) +{ + if (!_archive || !_current_entry) { + return 0; + } + + int flags = ARCHIVE_EXTRACT_TIME; + + struct archive *ext; + + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + + archive_entry_set_pathname(_current_entry, destpath.c_str()); + int r = archive_write_header(ext, _current_entry); + _current_entry = 0; + if (r != ARCHIVE_OK) { + fprintf (stderr, "Error reading archive: %s\n", archive_error_string(_archive)); + return -1; + } + + ar_copy_data (_archive, ext); + r = archive_write_finish_entry (ext); + if (r != ARCHIVE_OK) { + fprintf (stderr, "Error reading archive: %s\n", archive_error_string(_archive)); + return -1; + } + + return 0; +} + std::vector FileArchive::contents_file () { @@ -456,3 +533,21 @@ FileArchive::create (const std::map& filemap, Compress return 0; } + +struct archive* +FileArchive::setup_file_archive () +{ + struct archive* a = setup_archive (); + GStatBuf statbuf; + if (!g_stat (_req.url, &statbuf)) { + _req.mp.length = statbuf.st_size; + } else { + _req.mp.length = -1; + } + if (ARCHIVE_OK != archive_read_open_filename (a, _req.url, 8192)) { + fprintf (stderr, "Error opening archive: %s\n", archive_error_string(a)); + return 0; + } + + return a; +} diff --git a/libs/pbd/pbd/file_archive.h b/libs/pbd/pbd/file_archive.h index 85c75e52a7..7244a4e16e 100644 --- a/libs/pbd/pbd/file_archive.h +++ b/libs/pbd/pbd/file_archive.h @@ -33,10 +33,14 @@ class LIBPBD_API FileArchive { public: FileArchive (const std::string& url); + ~FileArchive (); int inflate (const std::string& destdir); std::vector contents (); + std::string next_file_name (); + int extract_current_file (const std::string& destpath); + /* these are mapped to libarchive's lzmaz * compression level 0..9 */ @@ -147,8 +151,13 @@ class LIBPBD_API FileArchive bool is_url (); + struct archive* setup_file_archive (); + Request _req; pthread_t _tid; + + struct archive_entry* _current_entry; + struct archive* _archive; }; } /* namespace */