diff options
Diffstat (limited to '')
22 files changed, 1294 insertions, 855 deletions
diff --git a/src/archive/ArchiveDomain.cxx b/src/archive/ArchiveDomain.cxx new file mode 100644 index 000000000..4adf4a886 --- /dev/null +++ b/src/archive/ArchiveDomain.cxx @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "ArchiveDomain.hxx" +#include "util/Domain.hxx" + +const Domain archive_domain("archive"); diff --git a/src/archive/ArchiveDomain.hxx b/src/archive/ArchiveDomain.hxx new file mode 100644 index 000000000..817ae5835 --- /dev/null +++ b/src/archive/ArchiveDomain.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_DOMAIN_HXX +#define MPD_ARCHIVE_DOMAIN_HXX + +extern const class Domain archive_domain; + +#endif diff --git a/src/archive/ArchiveFile.hxx b/src/archive/ArchiveFile.hxx new file mode 100644 index 000000000..473eef70b --- /dev/null +++ b/src/archive/ArchiveFile.hxx @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_FILE_HXX +#define MPD_ARCHIVE_FILE_HXX + +class Mutex; +class Cond; +class Error; +struct ArchivePlugin; +class ArchiveVisitor; +class InputStream; + +class ArchiveFile { +public: + const ArchivePlugin &plugin; + + ArchiveFile(const ArchivePlugin &_plugin) + :plugin(_plugin) {} + +protected: + /** + * Use Close() instead of delete. + */ + ~ArchiveFile() {} + +public: + virtual void Close() = 0; + + /** + * Visit all entries inside this archive. + */ + virtual void Visit(ArchiveVisitor &visitor) = 0; + + /** + * Opens an InputStream of a file within the archive. + * + * @param path the path within the archive + */ + virtual InputStream *OpenStream(const char *path, + Mutex &mutex, Cond &cond, + Error &error) = 0; +}; + +#endif diff --git a/src/archive/ArchiveList.cxx b/src/archive/ArchiveList.cxx new file mode 100644 index 000000000..79c3a16fe --- /dev/null +++ b/src/archive/ArchiveList.cxx @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ArchiveList.hxx" +#include "ArchivePlugin.hxx" +#include "util/StringUtil.hxx" +#include "plugins/Bzip2ArchivePlugin.hxx" +#include "plugins/Iso9660ArchivePlugin.hxx" +#include "plugins/ZzipArchivePlugin.hxx" +#include "util/Macros.hxx" + +#include <string.h> + +const ArchivePlugin *const archive_plugins[] = { +#ifdef HAVE_BZ2 + &bz2_archive_plugin, +#endif +#ifdef HAVE_ZZIP + &zzip_archive_plugin, +#endif +#ifdef HAVE_ISO9660 + &iso9660_archive_plugin, +#endif + nullptr +}; + +/** which plugins have been initialized successfully? */ +static bool archive_plugins_enabled[ARRAY_SIZE(archive_plugins) - 1]; + +#define archive_plugins_for_each_enabled(plugin) \ + archive_plugins_for_each(plugin) \ + if (archive_plugins_enabled[archive_plugin_iterator - archive_plugins]) + +const ArchivePlugin * +archive_plugin_from_suffix(const char *suffix) +{ + if (suffix == nullptr) + return nullptr; + + archive_plugins_for_each_enabled(plugin) + if (plugin->suffixes != nullptr && + string_array_contains(plugin->suffixes, suffix)) + return plugin; + + return nullptr; +} + +const ArchivePlugin * +archive_plugin_from_name(const char *name) +{ + archive_plugins_for_each_enabled(plugin) + if (strcmp(plugin->name, name) == 0) + return plugin; + + return nullptr; +} + +void archive_plugin_init_all(void) +{ + for (unsigned i = 0; archive_plugins[i] != nullptr; ++i) { + const ArchivePlugin *plugin = archive_plugins[i]; + if (plugin->init == nullptr || archive_plugins[i]->init()) + archive_plugins_enabled[i] = true; + } +} + +void archive_plugin_deinit_all(void) +{ + archive_plugins_for_each_enabled(plugin) + if (plugin->finish != nullptr) + plugin->finish(); +} + diff --git a/src/archive/ArchiveList.hxx b/src/archive/ArchiveList.hxx new file mode 100644 index 000000000..1f1b0ae96 --- /dev/null +++ b/src/archive/ArchiveList.hxx @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_LIST_HXX +#define MPD_ARCHIVE_LIST_HXX + +struct ArchivePlugin; + +extern const ArchivePlugin *const archive_plugins[]; + +#define archive_plugins_for_each(plugin) \ + for (const ArchivePlugin *plugin, \ + *const*archive_plugin_iterator = &archive_plugins[0]; \ + (plugin = *archive_plugin_iterator) != nullptr; \ + ++archive_plugin_iterator) + +/* interface for using plugins */ + +const ArchivePlugin * +archive_plugin_from_suffix(const char *suffix); + +const ArchivePlugin * +archive_plugin_from_name(const char *name); + +/* this is where we "load" all the "plugins" ;-) */ +void archive_plugin_init_all(void); + +/* this is where we "unload" all the "plugins" */ +void archive_plugin_deinit_all(void); + +#endif diff --git a/src/archive/ArchiveLookup.cxx b/src/archive/ArchiveLookup.cxx new file mode 100644 index 000000000..53730c504 --- /dev/null +++ b/src/archive/ArchiveLookup.cxx @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" /* must be first for large file support */ +#include "ArchiveLookup.hxx" +#include "ArchiveDomain.hxx" +#include "Log.hxx" + +#include <string.h> +#include <sys/stat.h> +#include <errno.h> + +gcc_pure +static char * +FindSlash(char *p, size_t i) +{ + for (; i > 0; --i) + if (p[i] == '/') + return p + i; + + return nullptr; +} + +gcc_pure +static const char * +FindSuffix(const char *p, const char *i) +{ + for (; i > p; --i) { + if (*i == '.') + return i + 1; + } + + return nullptr; +} + +bool +archive_lookup(char *pathname, const char **archive, + const char **inpath, const char **suffix) +{ + size_t idx = strlen(pathname); + + char *slash = nullptr; + + while (true) { + //try to stat if its real directory + struct stat st_info; + if (stat(pathname, &st_info) == -1) { + if (errno != ENOTDIR) { + FormatErrno(archive_domain, + "Failed to stat %s", pathname); + return false; + } + } else { + //is something found ins original path (is not an archive) + if (slash == nullptr) + return false; + + //its a file ? + if (S_ISREG(st_info.st_mode)) { + //so the upper should be file + *archive = pathname; + *inpath = slash + 1; + + //try to get suffix + *suffix = FindSuffix(pathname, slash - 1); + return true; + } else { + FormatError(archive_domain, + "Not a regular file: %s", + pathname); + return false; + } + } + + //find one dir up + if (slash != nullptr) + *slash = '/'; + + slash = FindSlash(pathname, idx - 1); + if (slash == nullptr) + return false; + + *slash = 0; + idx = slash - pathname; + } +} + diff --git a/src/archive/ArchiveLookup.hxx b/src/archive/ArchiveLookup.hxx new file mode 100644 index 000000000..0c08951a9 --- /dev/null +++ b/src/archive/ArchiveLookup.hxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_LOOKUP_HXX +#define MPD_ARCHIVE_LOOKUP_HXX + +/** + * + * archive_lookup is used to determine if part of pathname refers to an regular + * file (archive). If so then its also used to split pathname into archive file + * and path used to locate file in archive. It also returns suffix of the file. + * How it works: + * We do stat of the parent of input pathname as long as we find an regular file + * Normally this should never happen. When routine returns true pathname modified + * and split into archive, inpath and suffix. Otherwise nothing happens + * + * For example: + * + * /music/path/Talco.zip/Talco - Combat Circus/12 - A la pachenka.mp3 + * is split into archive: /music/path/Talco.zip + * inarchive pathname: Talco - Combat Circus/12 - A la pachenka.mp3 + * and suffix: zip + */ +bool +archive_lookup(char *pathname, const char **archive, + const char **inpath, const char **suffix); + +#endif + diff --git a/src/archive/ArchivePlugin.cxx b/src/archive/ArchivePlugin.cxx new file mode 100644 index 000000000..67f469e08 --- /dev/null +++ b/src/archive/ArchivePlugin.cxx @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "ArchivePlugin.hxx" +#include "ArchiveFile.hxx" +#include "fs/Path.hxx" +#include "util/Error.hxx" + +#include <assert.h> + +ArchiveFile * +archive_file_open(const ArchivePlugin *plugin, Path path, + Error &error) +{ + assert(plugin != nullptr); + assert(plugin->open != nullptr); + assert(!path.IsNull()); + + ArchiveFile *file = plugin->open(path, error); + assert((file == nullptr) == error.IsDefined()); + + return file; +} diff --git a/src/archive/ArchivePlugin.hxx b/src/archive/ArchivePlugin.hxx new file mode 100644 index 000000000..eb24bbdf9 --- /dev/null +++ b/src/archive/ArchivePlugin.hxx @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_PLUGIN_HXX +#define MPD_ARCHIVE_PLUGIN_HXX + +class ArchiveFile; +class Path; +class Error; + +struct ArchivePlugin { + const char *name; + + /** + * optional, set this to nullptr if the archive plugin doesn't + * have/need one this must false if there is an error and + * true otherwise + */ + bool (*init)(void); + + /** + * optional, set this to nullptr if the archive plugin doesn't + * have/need one + */ + void (*finish)(void); + + /** + * tryes to open archive file and associates handle with archive + * returns pointer to handle used is all operations with this archive + * or nullptr when opening fails + */ + ArchiveFile *(*open)(Path path_fs, Error &error); + + /** + * suffixes handled by this plugin. + * last element in these arrays must always be a nullptr + */ + const char *const*suffixes; +}; + +ArchiveFile * +archive_file_open(const ArchivePlugin *plugin, Path path, + Error &error); + +#endif diff --git a/src/archive/ArchiveVisitor.hxx b/src/archive/ArchiveVisitor.hxx new file mode 100644 index 000000000..6759695ca --- /dev/null +++ b/src/archive/ArchiveVisitor.hxx @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_VISITOR_HXX +#define MPD_ARCHIVE_VISITOR_HXX + +class ArchiveVisitor { +public: + virtual void VisitArchiveEntry(const char *path_utf8) = 0; +}; + +#endif diff --git a/src/archive/Bzip2ArchivePlugin.cxx b/src/archive/Bzip2ArchivePlugin.cxx deleted file mode 100644 index d1e6b51af..000000000 --- a/src/archive/Bzip2ArchivePlugin.cxx +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** - * single bz2 archive handling (requires libbz2) - */ - -#include "config.h" -#include "Bzip2ArchivePlugin.hxx" -#include "ArchivePlugin.hxx" -#include "ArchiveFile.hxx" -#include "ArchiveVisitor.hxx" -#include "InputStream.hxx" -#include "InputPlugin.hxx" -#include "util/RefCount.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" -#include "fs/Traits.hxx" - -#include <bzlib.h> - -#include <stdint.h> -#include <stddef.h> -#include <string.h> - -#ifdef HAVE_OLDER_BZIP2 -#define BZ2_bzDecompressInit bzDecompressInit -#define BZ2_bzDecompress bzDecompress -#endif - -class Bzip2ArchiveFile final : public ArchiveFile { -public: - RefCount ref; - - std::string name; - InputStream *const istream; - - Bzip2ArchiveFile(const char *path, InputStream *_is) - :ArchiveFile(bz2_archive_plugin), - name(PathTraits::GetBaseUTF8(path)), - istream(_is) { - // remove .bz2 suffix - const size_t len = name.length(); - if (len > 4) - name.erase(len - 4); - } - - ~Bzip2ArchiveFile() { - istream->Close(); - } - - void Ref() { - ref.Increment(); - } - - void Unref() { - if (!ref.Decrement()) - return; - - delete this; - } - - virtual void Close() override { - Unref(); - } - - virtual void Visit(ArchiveVisitor &visitor) override { - visitor.VisitArchiveEntry(name.c_str()); - } - - virtual InputStream *OpenStream(const char *path, - Mutex &mutex, Cond &cond, - Error &error) override; -}; - -struct Bzip2InputStream { - InputStream base; - - Bzip2ArchiveFile *archive; - - bool eof; - - bz_stream bzstream; - - char buffer[5000]; - - Bzip2InputStream(Bzip2ArchiveFile &context, const char *uri, - Mutex &mutex, Cond &cond); - ~Bzip2InputStream(); - - bool Open(Error &error); - void Close(); -}; - -extern const InputPlugin bz2_inputplugin; - -static constexpr Domain bz2_domain("bz2"); - -/* single archive handling allocation helpers */ - -inline bool -Bzip2InputStream::Open(Error &error) -{ - bzstream.bzalloc = nullptr; - bzstream.bzfree = nullptr; - bzstream.opaque = nullptr; - - bzstream.next_in = (char *)buffer; - bzstream.avail_in = 0; - - int ret = BZ2_bzDecompressInit(&bzstream, 0, 0); - if (ret != BZ_OK) { - error.Set(bz2_domain, ret, - "BZ2_bzDecompressInit() has failed"); - return false; - } - - base.ready = true; - return true; -} - -inline void -Bzip2InputStream::Close() -{ - BZ2_bzDecompressEnd(&bzstream); -} - -/* archive open && listing routine */ - -static ArchiveFile * -bz2_open(const char *pathname, Error &error) -{ - static Mutex mutex; - static Cond cond; - InputStream *is = InputStream::Open(pathname, mutex, cond, error); - if (is == nullptr) - return nullptr; - - return new Bzip2ArchiveFile(pathname, is); -} - -/* single archive handling */ - -Bzip2InputStream::Bzip2InputStream(Bzip2ArchiveFile &_context, const char *uri, - Mutex &mutex, Cond &cond) - :base(bz2_inputplugin, uri, mutex, cond), - archive(&_context), eof(false) -{ - archive->Ref(); -} - -Bzip2InputStream::~Bzip2InputStream() -{ - archive->Unref(); -} - -InputStream * -Bzip2ArchiveFile::OpenStream(const char *path, - Mutex &mutex, Cond &cond, - Error &error) -{ - Bzip2InputStream *bis = new Bzip2InputStream(*this, path, mutex, cond); - if (!bis->Open(error)) { - delete bis; - return nullptr; - } - - return &bis->base; -} - -static void -bz2_is_close(InputStream *is) -{ - Bzip2InputStream *bis = (Bzip2InputStream *)is; - - bis->Close(); - delete bis; -} - -static bool -bz2_fillbuffer(Bzip2InputStream *bis, Error &error) -{ - size_t count; - bz_stream *bzstream; - - bzstream = &bis->bzstream; - - if (bzstream->avail_in > 0) - return true; - - count = bis->archive->istream->Read(bis->buffer, sizeof(bis->buffer), - error); - if (count == 0) - return false; - - bzstream->next_in = bis->buffer; - bzstream->avail_in = count; - return true; -} - -static size_t -bz2_is_read(InputStream *is, void *ptr, size_t length, - Error &error) -{ - Bzip2InputStream *bis = (Bzip2InputStream *)is; - bz_stream *bzstream; - int bz_result; - size_t nbytes = 0; - - if (bis->eof) - return 0; - - bzstream = &bis->bzstream; - bzstream->next_out = (char *)ptr; - bzstream->avail_out = length; - - do { - if (!bz2_fillbuffer(bis, error)) - return 0; - - bz_result = BZ2_bzDecompress(bzstream); - - if (bz_result == BZ_STREAM_END) { - bis->eof = true; - break; - } - - if (bz_result != BZ_OK) { - error.Set(bz2_domain, bz_result, - "BZ2_bzDecompress() has failed"); - return 0; - } - } while (bzstream->avail_out == length); - - nbytes = length - bzstream->avail_out; - is->offset += nbytes; - - return nbytes; -} - -static bool -bz2_is_eof(InputStream *is) -{ - Bzip2InputStream *bis = (Bzip2InputStream *)is; - - return bis->eof; -} - -/* exported structures */ - -static const char *const bz2_extensions[] = { - "bz2", - nullptr -}; - -const InputPlugin bz2_inputplugin = { - nullptr, - nullptr, - nullptr, - nullptr, - bz2_is_close, - nullptr, - nullptr, - nullptr, - nullptr, - bz2_is_read, - bz2_is_eof, - nullptr, -}; - -const struct archive_plugin bz2_archive_plugin = { - "bz2", - nullptr, - nullptr, - bz2_open, - bz2_extensions, -}; - diff --git a/src/archive/Bzip2ArchivePlugin.hxx b/src/archive/Bzip2ArchivePlugin.hxx deleted file mode 100644 index a7933a7a7..000000000 --- a/src/archive/Bzip2ArchivePlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ARCHIVE_BZ2_HXX -#define MPD_ARCHIVE_BZ2_HXX - -extern const struct archive_plugin bz2_archive_plugin; - -#endif diff --git a/src/archive/Iso9660ArchivePlugin.cxx b/src/archive/Iso9660ArchivePlugin.cxx deleted file mode 100644 index ad21d4a3d..000000000 --- a/src/archive/Iso9660ArchivePlugin.cxx +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** - * iso archive handling (requires cdio, and iso9660) - */ - -#include "config.h" -#include "Iso9660ArchivePlugin.hxx" -#include "ArchivePlugin.hxx" -#include "ArchiveFile.hxx" -#include "ArchiveVisitor.hxx" -#include "InputStream.hxx" -#include "InputPlugin.hxx" -#include "util/RefCount.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" - -#include <cdio/cdio.h> -#include <cdio/iso9660.h> - -#include <stdlib.h> -#include <string.h> - -#define CEILING(x, y) ((x+(y-1))/y) - -class Iso9660ArchiveFile final : public ArchiveFile { -public: - RefCount ref; - - iso9660_t *iso; - - Iso9660ArchiveFile(iso9660_t *_iso) - :ArchiveFile(iso9660_archive_plugin), iso(_iso) {} - - ~Iso9660ArchiveFile() { - iso9660_close(iso); - } - - void Unref() { - if (ref.Decrement()) - delete this; - } - - void Visit(const char *path, ArchiveVisitor &visitor); - - virtual void Close() override { - Unref(); - } - - virtual void Visit(ArchiveVisitor &visitor) override; - - virtual InputStream *OpenStream(const char *path, - Mutex &mutex, Cond &cond, - Error &error) override; -}; - -extern const InputPlugin iso9660_input_plugin; - -static constexpr Domain iso9660_domain("iso9660"); - -/* archive open && listing routine */ - -inline void -Iso9660ArchiveFile::Visit(const char *psz_path, ArchiveVisitor &visitor) -{ - CdioList_t *entlist; - CdioListNode_t *entnode; - iso9660_stat_t *statbuf; - char pathname[4096]; - - entlist = iso9660_ifs_readdir (iso, psz_path); - if (!entlist) { - return; - } - /* Iterate over the list of nodes that iso9660_ifs_readdir gives */ - _CDIO_LIST_FOREACH (entnode, entlist) { - statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode); - - strcpy(pathname, psz_path); - strcat(pathname, statbuf->filename); - - if (iso9660_stat_s::_STAT_DIR == statbuf->type ) { - if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) { - strcat(pathname, "/"); - Visit(pathname, visitor); - } - } else { - //remove leading / - visitor.VisitArchiveEntry(pathname + 1); - } - } - _cdio_list_free (entlist, true); -} - -static ArchiveFile * -iso9660_archive_open(const char *pathname, Error &error) -{ - /* open archive */ - auto iso = iso9660_open(pathname); - if (iso == nullptr) { - error.Format(iso9660_domain, - "Failed to open ISO9660 file %s", pathname); - return nullptr; - } - - return new Iso9660ArchiveFile(iso); -} - -void -Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor) -{ - Visit("/", visitor); -} - -/* single archive handling */ - -struct Iso9660InputStream { - InputStream base; - - Iso9660ArchiveFile *archive; - - iso9660_stat_t *statbuf; - size_t max_blocks; - - Iso9660InputStream(Iso9660ArchiveFile &_archive, const char *uri, - Mutex &mutex, Cond &cond, - iso9660_stat_t *_statbuf) - :base(iso9660_input_plugin, uri, mutex, cond), - archive(&_archive), statbuf(_statbuf), - max_blocks(CEILING(statbuf->size, ISO_BLOCKSIZE)) { - - base.ready = true; - base.size = statbuf->size; - - archive->ref.Increment(); - } - - ~Iso9660InputStream() { - free(statbuf); - archive->Unref(); - } -}; - -InputStream * -Iso9660ArchiveFile::OpenStream(const char *pathname, - Mutex &mutex, Cond &cond, - Error &error) -{ - auto statbuf = iso9660_ifs_stat_translate(iso, pathname); - if (statbuf == nullptr) { - error.Format(iso9660_domain, - "not found in the ISO file: %s", pathname); - return nullptr; - } - - Iso9660InputStream *iis = - new Iso9660InputStream(*this, pathname, mutex, cond, - statbuf); - return &iis->base; -} - -static void -iso9660_input_close(InputStream *is) -{ - Iso9660InputStream *iis = (Iso9660InputStream *)is; - - delete iis; -} - - -static size_t -iso9660_input_read(InputStream *is, void *ptr, size_t size, - Error &error) -{ - Iso9660InputStream *iis = (Iso9660InputStream *)is; - int readed = 0; - int no_blocks, cur_block; - size_t left_bytes = iis->statbuf->size - is->offset; - - size = (size * ISO_BLOCKSIZE) / ISO_BLOCKSIZE; - - if (left_bytes < size) { - no_blocks = CEILING(left_bytes,ISO_BLOCKSIZE); - } else { - no_blocks = size / ISO_BLOCKSIZE; - } - if (no_blocks > 0) { - - cur_block = is->offset / ISO_BLOCKSIZE; - - readed = iso9660_iso_seek_read (iis->archive->iso, ptr, - iis->statbuf->lsn + cur_block, no_blocks); - - if (readed != no_blocks * ISO_BLOCKSIZE) { - error.Format(iso9660_domain, - "error reading ISO file at lsn %lu", - (unsigned long)cur_block); - return 0; - } - if (left_bytes < size) { - readed = left_bytes; - } - - is->offset += readed; - } - return readed; -} - -static bool -iso9660_input_eof(InputStream *is) -{ - return is->offset == is->size; -} - -/* exported structures */ - -static const char *const iso9660_archive_extensions[] = { - "iso", - nullptr -}; - -const InputPlugin iso9660_input_plugin = { - nullptr, - nullptr, - nullptr, - nullptr, - iso9660_input_close, - nullptr, - nullptr, - nullptr, - nullptr, - iso9660_input_read, - iso9660_input_eof, - nullptr, -}; - -const struct archive_plugin iso9660_archive_plugin = { - "iso", - nullptr, - nullptr, - iso9660_archive_open, - iso9660_archive_extensions, -}; diff --git a/src/archive/Iso9660ArchivePlugin.hxx b/src/archive/Iso9660ArchivePlugin.hxx deleted file mode 100644 index 6fbab6159..000000000 --- a/src/archive/Iso9660ArchivePlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ARCHIVE_ISO9660_HXX -#define MPD_ARCHIVE_ISO9660_HXX - -extern const struct archive_plugin iso9660_archive_plugin; - -#endif diff --git a/src/archive/ZzipArchivePlugin.cxx b/src/archive/ZzipArchivePlugin.cxx deleted file mode 100644 index d3e4cc837..000000000 --- a/src/archive/ZzipArchivePlugin.cxx +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/** - * zip archive handling (requires zziplib) - */ - -#include "config.h" -#include "ZzipArchivePlugin.hxx" -#include "ArchivePlugin.hxx" -#include "ArchiveFile.hxx" -#include "ArchiveVisitor.hxx" -#include "InputStream.hxx" -#include "InputPlugin.hxx" -#include "util/RefCount.hxx" -#include "util/Error.hxx" -#include "util/Domain.hxx" - -#include <zzip/zzip.h> - -#include <string.h> - -class ZzipArchiveFile final : public ArchiveFile { -public: - RefCount ref; - - ZZIP_DIR *const dir; - - ZzipArchiveFile(ZZIP_DIR *_dir) - :ArchiveFile(zzip_archive_plugin), dir(_dir) {} - - ~ZzipArchiveFile() { - zzip_dir_close(dir); - } - - void Unref() { - if (ref.Decrement()) - delete this; - } - - virtual void Close() override { - Unref(); - } - - virtual void Visit(ArchiveVisitor &visitor) override; - - virtual InputStream *OpenStream(const char *path, - Mutex &mutex, Cond &cond, - Error &error) override; -}; - -extern const InputPlugin zzip_input_plugin; - -static constexpr Domain zzip_domain("zzip"); - -/* archive open && listing routine */ - -static ArchiveFile * -zzip_archive_open(const char *pathname, Error &error) -{ - ZZIP_DIR *dir = zzip_dir_open(pathname, nullptr); - if (dir == nullptr) { - error.Format(zzip_domain, "Failed to open ZIP file %s", - pathname); - return nullptr; - } - - return new ZzipArchiveFile(dir); -} - -inline void -ZzipArchiveFile::Visit(ArchiveVisitor &visitor) -{ - zzip_rewinddir(dir); - - ZZIP_DIRENT dirent; - while (zzip_dir_read(dir, &dirent)) - //add only files - if (dirent.st_size > 0) - visitor.VisitArchiveEntry(dirent.d_name); -} - -/* single archive handling */ - -struct ZzipInputStream { - InputStream base; - - ZzipArchiveFile *archive; - - ZZIP_FILE *file; - - ZzipInputStream(ZzipArchiveFile &_archive, const char *uri, - Mutex &mutex, Cond &cond, - ZZIP_FILE *_file) - :base(zzip_input_plugin, uri, mutex, cond), - archive(&_archive), file(_file) { - base.ready = true; - //we are seekable (but its not recommendent to do so) - base.seekable = true; - - ZZIP_STAT z_stat; - zzip_file_stat(file, &z_stat); - base.size = z_stat.st_size; - - archive->ref.Increment(); - } - - ~ZzipInputStream() { - zzip_file_close(file); - archive->Unref(); - } -}; - -InputStream * -ZzipArchiveFile::OpenStream(const char *pathname, - Mutex &mutex, Cond &cond, - Error &error) -{ - ZZIP_FILE *_file = zzip_file_open(dir, pathname, 0); - if (_file == nullptr) { - error.Format(zzip_domain, "not found in the ZIP file: %s", - pathname); - return nullptr; - } - - ZzipInputStream *zis = - new ZzipInputStream(*this, pathname, - mutex, cond, - _file); - return &zis->base; -} - -static void -zzip_input_close(InputStream *is) -{ - ZzipInputStream *zis = (ZzipInputStream *)is; - - delete zis; -} - -static size_t -zzip_input_read(InputStream *is, void *ptr, size_t size, - Error &error) -{ - ZzipInputStream *zis = (ZzipInputStream *)is; - int ret; - - ret = zzip_file_read(zis->file, ptr, size); - if (ret < 0) { - error.Set(zzip_domain, "zzip_file_read() has failed"); - return 0; - } - - is->offset = zzip_tell(zis->file); - - return ret; -} - -static bool -zzip_input_eof(InputStream *is) -{ - ZzipInputStream *zis = (ZzipInputStream *)is; - - return (InputPlugin::offset_type)zzip_tell(zis->file) == is->size; -} - -static bool -zzip_input_seek(InputStream *is, InputPlugin::offset_type offset, - int whence, Error &error) -{ - ZzipInputStream *zis = (ZzipInputStream *)is; - zzip_off_t ofs = zzip_seek(zis->file, offset, whence); - if (ofs < 0) { - error.Set(zzip_domain, "zzip_seek() has failed"); - return false; - } - - is->offset = ofs; - return true; -} - -/* exported structures */ - -static const char *const zzip_archive_extensions[] = { - "zip", - nullptr -}; - -const InputPlugin zzip_input_plugin = { - nullptr, - nullptr, - nullptr, - nullptr, - zzip_input_close, - nullptr, - nullptr, - nullptr, - nullptr, - zzip_input_read, - zzip_input_eof, - zzip_input_seek, -}; - -const struct archive_plugin zzip_archive_plugin = { - "zzip", - nullptr, - nullptr, - zzip_archive_open, - zzip_archive_extensions, -}; diff --git a/src/archive/ZzipArchivePlugin.hxx b/src/archive/ZzipArchivePlugin.hxx deleted file mode 100644 index 4ba16849b..000000000 --- a/src/archive/ZzipArchivePlugin.hxx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2003-2013 The Music Player Daemon Project - * http://www.musicpd.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPD_ARCHIVE_ZZIP_HXX -#define MPD_ARCHIVE_ZZIP_HXX - -extern const struct archive_plugin zzip_archive_plugin; - -#endif diff --git a/src/archive/plugins/Bzip2ArchivePlugin.cxx b/src/archive/plugins/Bzip2ArchivePlugin.cxx new file mode 100644 index 000000000..2b92049dd --- /dev/null +++ b/src/archive/plugins/Bzip2ArchivePlugin.cxx @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * single bz2 archive handling (requires libbz2) + */ + +#include "config.h" +#include "Bzip2ArchivePlugin.hxx" +#include "../ArchivePlugin.hxx" +#include "../ArchiveFile.hxx" +#include "../ArchiveVisitor.hxx" +#include "input/InputStream.hxx" +#include "input/InputPlugin.hxx" +#include "input/LocalOpen.hxx" +#include "util/RefCount.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "fs/Traits.hxx" +#include "fs/Path.hxx" + +#include <bzlib.h> + +#include <stddef.h> + +#ifdef HAVE_OLDER_BZIP2 +#define BZ2_bzDecompressInit bzDecompressInit +#define BZ2_bzDecompress bzDecompress +#endif + +class Bzip2ArchiveFile final : public ArchiveFile { +public: + RefCount ref; + + std::string name; + InputStream *const istream; + + Bzip2ArchiveFile(Path path, InputStream *_is) + :ArchiveFile(bz2_archive_plugin), + name(PathTraitsFS::GetBase(path.c_str())), + istream(_is) { + // remove .bz2 suffix + const size_t len = name.length(); + if (len > 4) + name.erase(len - 4); + } + + ~Bzip2ArchiveFile() { + delete istream; + } + + void Ref() { + ref.Increment(); + } + + void Unref() { + if (!ref.Decrement()) + return; + + delete this; + } + + virtual void Close() override { + Unref(); + } + + virtual void Visit(ArchiveVisitor &visitor) override { + visitor.VisitArchiveEntry(name.c_str()); + } + + virtual InputStream *OpenStream(const char *path, + Mutex &mutex, Cond &cond, + Error &error) override; +}; + +struct Bzip2InputStream final : public InputStream { + Bzip2ArchiveFile *archive; + + bool eof; + + bz_stream bzstream; + + char buffer[5000]; + + Bzip2InputStream(Bzip2ArchiveFile &context, const char *uri, + Mutex &mutex, Cond &cond); + ~Bzip2InputStream(); + + bool Open(Error &error); + + /* virtual methods from InputStream */ + bool IsEOF() override; + size_t Read(void *ptr, size_t size, Error &error) override; +}; + +static constexpr Domain bz2_domain("bz2"); + +/* single archive handling allocation helpers */ + +inline bool +Bzip2InputStream::Open(Error &error) +{ + bzstream.bzalloc = nullptr; + bzstream.bzfree = nullptr; + bzstream.opaque = nullptr; + + bzstream.next_in = (char *)buffer; + bzstream.avail_in = 0; + + int ret = BZ2_bzDecompressInit(&bzstream, 0, 0); + if (ret != BZ_OK) { + error.Set(bz2_domain, ret, + "BZ2_bzDecompressInit() has failed"); + return false; + } + + SetReady(); + return true; +} + +/* archive open && listing routine */ + +static ArchiveFile * +bz2_open(Path pathname, Error &error) +{ + static Mutex mutex; + static Cond cond; + InputStream *is = OpenLocalInputStream(pathname, mutex, cond, error); + if (is == nullptr) + return nullptr; + + return new Bzip2ArchiveFile(pathname, is); +} + +/* single archive handling */ + +Bzip2InputStream::Bzip2InputStream(Bzip2ArchiveFile &_context, + const char *_uri, + Mutex &_mutex, Cond &_cond) + :InputStream(_uri, _mutex, _cond), + archive(&_context), eof(false) +{ + archive->Ref(); +} + +Bzip2InputStream::~Bzip2InputStream() +{ + BZ2_bzDecompressEnd(&bzstream); + archive->Unref(); +} + +InputStream * +Bzip2ArchiveFile::OpenStream(const char *path, + Mutex &mutex, Cond &cond, + Error &error) +{ + Bzip2InputStream *bis = new Bzip2InputStream(*this, path, mutex, cond); + if (!bis->Open(error)) { + delete bis; + return nullptr; + } + + return bis; +} + +static bool +bz2_fillbuffer(Bzip2InputStream *bis, Error &error) +{ + size_t count; + bz_stream *bzstream; + + bzstream = &bis->bzstream; + + if (bzstream->avail_in > 0) + return true; + + count = bis->archive->istream->Read(bis->buffer, sizeof(bis->buffer), + error); + if (count == 0) + return false; + + bzstream->next_in = bis->buffer; + bzstream->avail_in = count; + return true; +} + +size_t +Bzip2InputStream::Read(void *ptr, size_t length, Error &error) +{ + int bz_result; + size_t nbytes = 0; + + if (eof) + return 0; + + bzstream.next_out = (char *)ptr; + bzstream.avail_out = length; + + do { + if (!bz2_fillbuffer(this, error)) + return 0; + + bz_result = BZ2_bzDecompress(&bzstream); + + if (bz_result == BZ_STREAM_END) { + eof = true; + break; + } + + if (bz_result != BZ_OK) { + error.Set(bz2_domain, bz_result, + "BZ2_bzDecompress() has failed"); + return 0; + } + } while (bzstream.avail_out == length); + + nbytes = length - bzstream.avail_out; + offset += nbytes; + + return nbytes; +} + +bool +Bzip2InputStream::IsEOF() +{ + return eof; +} + +/* exported structures */ + +static const char *const bz2_extensions[] = { + "bz2", + nullptr +}; + +const ArchivePlugin bz2_archive_plugin = { + "bz2", + nullptr, + nullptr, + bz2_open, + bz2_extensions, +}; + diff --git a/src/archive/plugins/Bzip2ArchivePlugin.hxx b/src/archive/plugins/Bzip2ArchivePlugin.hxx new file mode 100644 index 000000000..1a0a578d1 --- /dev/null +++ b/src/archive/plugins/Bzip2ArchivePlugin.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_BZ2_HXX +#define MPD_ARCHIVE_BZ2_HXX + +struct ArchivePlugin; + +extern const ArchivePlugin bz2_archive_plugin; + +#endif diff --git a/src/archive/plugins/Iso9660ArchivePlugin.cxx b/src/archive/plugins/Iso9660ArchivePlugin.cxx new file mode 100644 index 000000000..ba415d3c5 --- /dev/null +++ b/src/archive/plugins/Iso9660ArchivePlugin.cxx @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * iso archive handling (requires cdio, and iso9660) + */ + +#include "config.h" +#include "Iso9660ArchivePlugin.hxx" +#include "../ArchivePlugin.hxx" +#include "../ArchiveFile.hxx" +#include "../ArchiveVisitor.hxx" +#include "input/InputStream.hxx" +#include "input/InputPlugin.hxx" +#include "fs/Path.hxx" +#include "util/RefCount.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +#include <cdio/iso9660.h> + +#include <stdlib.h> +#include <string.h> + +#define CEILING(x, y) ((x+(y-1))/y) + +class Iso9660ArchiveFile final : public ArchiveFile { + RefCount ref; + + iso9660_t *iso; + +public: + Iso9660ArchiveFile(iso9660_t *_iso) + :ArchiveFile(iso9660_archive_plugin), iso(_iso) {} + + ~Iso9660ArchiveFile() { + iso9660_close(iso); + } + + void Ref() { + ref.Increment(); + } + + void Unref() { + if (ref.Decrement()) + delete this; + } + + long SeekRead(void *ptr, lsn_t start, long int i_size) const { + return iso9660_iso_seek_read(iso, ptr, start, i_size); + } + + void Visit(const char *path, ArchiveVisitor &visitor); + + virtual void Close() override { + Unref(); + } + + virtual void Visit(ArchiveVisitor &visitor) override; + + virtual InputStream *OpenStream(const char *path, + Mutex &mutex, Cond &cond, + Error &error) override; +}; + +static constexpr Domain iso9660_domain("iso9660"); + +/* archive open && listing routine */ + +inline void +Iso9660ArchiveFile::Visit(const char *psz_path, ArchiveVisitor &visitor) +{ + CdioList_t *entlist; + CdioListNode_t *entnode; + iso9660_stat_t *statbuf; + char pathname[4096]; + + entlist = iso9660_ifs_readdir (iso, psz_path); + if (!entlist) { + return; + } + /* Iterate over the list of nodes that iso9660_ifs_readdir gives */ + _CDIO_LIST_FOREACH (entnode, entlist) { + statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode); + + strcpy(pathname, psz_path); + strcat(pathname, statbuf->filename); + + if (iso9660_stat_s::_STAT_DIR == statbuf->type ) { + if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) { + strcat(pathname, "/"); + Visit(pathname, visitor); + } + } else { + //remove leading / + visitor.VisitArchiveEntry(pathname + 1); + } + } + _cdio_list_free (entlist, true); +} + +static ArchiveFile * +iso9660_archive_open(Path pathname, Error &error) +{ + /* open archive */ + auto iso = iso9660_open(pathname.c_str()); + if (iso == nullptr) { + error.Format(iso9660_domain, + "Failed to open ISO9660 file %s", + pathname.c_str()); + return nullptr; + } + + return new Iso9660ArchiveFile(iso); +} + +void +Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor) +{ + Visit("/", visitor); +} + +/* single archive handling */ + +class Iso9660InputStream final : public InputStream { + Iso9660ArchiveFile &archive; + + iso9660_stat_t *statbuf; + +public: + Iso9660InputStream(Iso9660ArchiveFile &_archive, const char *_uri, + Mutex &_mutex, Cond &_cond, + iso9660_stat_t *_statbuf) + :InputStream(_uri, _mutex, _cond), + archive(_archive), statbuf(_statbuf) { + size = statbuf->size; + SetReady(); + + archive.Ref(); + } + + ~Iso9660InputStream() { + free(statbuf); + archive.Unref(); + } + + /* virtual methods from InputStream */ + bool IsEOF() override; + size_t Read(void *ptr, size_t size, Error &error) override; +}; + +InputStream * +Iso9660ArchiveFile::OpenStream(const char *pathname, + Mutex &mutex, Cond &cond, + Error &error) +{ + auto statbuf = iso9660_ifs_stat_translate(iso, pathname); + if (statbuf == nullptr) { + error.Format(iso9660_domain, + "not found in the ISO file: %s", pathname); + return nullptr; + } + + return new Iso9660InputStream(*this, pathname, mutex, cond, + statbuf); +} + +size_t +Iso9660InputStream::Read(void *ptr, size_t read_size, Error &error) +{ + int readed = 0; + int no_blocks, cur_block; + size_t left_bytes = statbuf->size - offset; + + if (left_bytes < read_size) { + no_blocks = CEILING(left_bytes, ISO_BLOCKSIZE); + } else { + no_blocks = read_size / ISO_BLOCKSIZE; + } + + if (no_blocks == 0) + return 0; + + cur_block = offset / ISO_BLOCKSIZE; + + readed = archive.SeekRead(ptr, statbuf->lsn + cur_block, + no_blocks); + + if (readed != no_blocks * ISO_BLOCKSIZE) { + error.Format(iso9660_domain, + "error reading ISO file at lsn %lu", + (unsigned long)cur_block); + return 0; + } + if (left_bytes < read_size) { + readed = left_bytes; + } + + offset += readed; + return readed; +} + +bool +Iso9660InputStream::IsEOF() +{ + return offset == size; +} + +/* exported structures */ + +static const char *const iso9660_archive_extensions[] = { + "iso", + nullptr +}; + +const ArchivePlugin iso9660_archive_plugin = { + "iso", + nullptr, + nullptr, + iso9660_archive_open, + iso9660_archive_extensions, +}; diff --git a/src/archive/plugins/Iso9660ArchivePlugin.hxx b/src/archive/plugins/Iso9660ArchivePlugin.hxx new file mode 100644 index 000000000..9335e83b3 --- /dev/null +++ b/src/archive/plugins/Iso9660ArchivePlugin.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_ISO9660_HXX +#define MPD_ARCHIVE_ISO9660_HXX + +struct ArchivePlugin; + +extern const ArchivePlugin iso9660_archive_plugin; + +#endif diff --git a/src/archive/plugins/ZzipArchivePlugin.cxx b/src/archive/plugins/ZzipArchivePlugin.cxx new file mode 100644 index 000000000..21cb693d8 --- /dev/null +++ b/src/archive/plugins/ZzipArchivePlugin.cxx @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/** + * zip archive handling (requires zziplib) + */ + +#include "config.h" +#include "ZzipArchivePlugin.hxx" +#include "../ArchivePlugin.hxx" +#include "../ArchiveFile.hxx" +#include "../ArchiveVisitor.hxx" +#include "input/InputStream.hxx" +#include "input/InputPlugin.hxx" +#include "fs/Path.hxx" +#include "util/RefCount.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +#include <zzip/zzip.h> + +class ZzipArchiveFile final : public ArchiveFile { +public: + RefCount ref; + + ZZIP_DIR *const dir; + + ZzipArchiveFile(ZZIP_DIR *_dir) + :ArchiveFile(zzip_archive_plugin), dir(_dir) {} + + ~ZzipArchiveFile() { + zzip_dir_close(dir); + } + + void Unref() { + if (ref.Decrement()) + delete this; + } + + virtual void Close() override { + Unref(); + } + + virtual void Visit(ArchiveVisitor &visitor) override; + + virtual InputStream *OpenStream(const char *path, + Mutex &mutex, Cond &cond, + Error &error) override; +}; + +static constexpr Domain zzip_domain("zzip"); + +/* archive open && listing routine */ + +static ArchiveFile * +zzip_archive_open(Path pathname, Error &error) +{ + ZZIP_DIR *dir = zzip_dir_open(pathname.c_str(), nullptr); + if (dir == nullptr) { + error.Format(zzip_domain, "Failed to open ZIP file %s", + pathname.c_str()); + return nullptr; + } + + return new ZzipArchiveFile(dir); +} + +inline void +ZzipArchiveFile::Visit(ArchiveVisitor &visitor) +{ + zzip_rewinddir(dir); + + ZZIP_DIRENT dirent; + while (zzip_dir_read(dir, &dirent)) + //add only files + if (dirent.st_size > 0) + visitor.VisitArchiveEntry(dirent.d_name); +} + +/* single archive handling */ + +struct ZzipInputStream final : public InputStream { + ZzipArchiveFile *archive; + + ZZIP_FILE *file; + + ZzipInputStream(ZzipArchiveFile &_archive, const char *_uri, + Mutex &_mutex, Cond &_cond, + ZZIP_FILE *_file) + :InputStream(_uri, _mutex, _cond), + archive(&_archive), file(_file) { + //we are seekable (but its not recommendent to do so) + seekable = true; + + ZZIP_STAT z_stat; + zzip_file_stat(file, &z_stat); + size = z_stat.st_size; + + SetReady(); + + archive->ref.Increment(); + } + + ~ZzipInputStream() { + zzip_file_close(file); + archive->Unref(); + } + + /* virtual methods from InputStream */ + bool IsEOF() override; + size_t Read(void *ptr, size_t size, Error &error) override; + bool Seek(offset_type offset, Error &error) override; +}; + +InputStream * +ZzipArchiveFile::OpenStream(const char *pathname, + Mutex &mutex, Cond &cond, + Error &error) +{ + ZZIP_FILE *_file = zzip_file_open(dir, pathname, 0); + if (_file == nullptr) { + error.Format(zzip_domain, "not found in the ZIP file: %s", + pathname); + return nullptr; + } + + return new ZzipInputStream(*this, pathname, + mutex, cond, + _file); +} + +size_t +ZzipInputStream::Read(void *ptr, size_t read_size, Error &error) +{ + int ret = zzip_file_read(file, ptr, read_size); + if (ret < 0) { + error.Set(zzip_domain, "zzip_file_read() has failed"); + return 0; + } + + offset = zzip_tell(file); + return ret; +} + +bool +ZzipInputStream::IsEOF() +{ + return offset_type(zzip_tell(file)) == size; +} + +bool +ZzipInputStream::Seek(offset_type new_offset, Error &error) +{ + zzip_off_t ofs = zzip_seek(file, new_offset, SEEK_SET); + if (ofs < 0) { + error.Set(zzip_domain, "zzip_seek() has failed"); + return false; + } + + offset = ofs; + return true; +} + +/* exported structures */ + +static const char *const zzip_archive_extensions[] = { + "zip", + nullptr +}; + +const ArchivePlugin zzip_archive_plugin = { + "zzip", + nullptr, + nullptr, + zzip_archive_open, + zzip_archive_extensions, +}; diff --git a/src/archive/plugins/ZzipArchivePlugin.hxx b/src/archive/plugins/ZzipArchivePlugin.hxx new file mode 100644 index 000000000..cc92b7c52 --- /dev/null +++ b/src/archive/plugins/ZzipArchivePlugin.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2014 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ARCHIVE_ZZIP_HXX +#define MPD_ARCHIVE_ZZIP_HXX + +struct ArchivePlugin; + +extern const ArchivePlugin zzip_archive_plugin; + +#endif |