aboutsummaryrefslogtreecommitdiffstats
path: root/src/input
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2014-01-24 16:18:21 +0100
committerMax Kellermann <max@duempel.org>2014-01-24 16:33:33 +0100
commitf8bfea8bae44134e8002db869fd4ec137d1c0d6e (patch)
treed370391baccbbc4c23306656aec3751a7ffc95ae /src/input
parente199c33c6e9eb2f1aa588073f49f44de274985d5 (diff)
downloadmpd-f8bfea8bae44134e8002db869fd4ec137d1c0d6e.tar.gz
mpd-f8bfea8bae44134e8002db869fd4ec137d1c0d6e.tar.xz
mpd-f8bfea8bae44134e8002db869fd4ec137d1c0d6e.zip
Input*: move to input/
Diffstat (limited to 'src/input')
-rw-r--r--src/input/Init.cxx102
-rw-r--r--src/input/Init.hxx36
-rw-r--r--src/input/InputPlugin.hxx91
-rw-r--r--src/input/InputStream.cxx214
-rw-r--r--src/input/InputStream.hxx301
-rw-r--r--src/input/Registry.cxx86
-rw-r--r--src/input/Registry.hxx43
-rw-r--r--src/input/TextInputStream.cxx78
-rw-r--r--src/input/TextInputStream.hxx56
-rw-r--r--src/input/plugins/AlsaInputPlugin.cxx (renamed from src/input/AlsaInputPlugin.cxx)4
-rw-r--r--src/input/plugins/AlsaInputPlugin.hxx (renamed from src/input/AlsaInputPlugin.hxx)2
-rw-r--r--src/input/plugins/ArchiveInputPlugin.cxx (renamed from src/input/ArchiveInputPlugin.cxx)2
-rw-r--r--src/input/plugins/ArchiveInputPlugin.hxx (renamed from src/input/ArchiveInputPlugin.hxx)0
-rw-r--r--src/input/plugins/CdioParanoiaInputPlugin.cxx (renamed from src/input/CdioParanoiaInputPlugin.cxx)4
-rw-r--r--src/input/plugins/CdioParanoiaInputPlugin.hxx (renamed from src/input/CdioParanoiaInputPlugin.hxx)0
-rw-r--r--src/input/plugins/CurlInputPlugin.cxx (renamed from src/input/CurlInputPlugin.cxx)4
-rw-r--r--src/input/plugins/CurlInputPlugin.hxx (renamed from src/input/CurlInputPlugin.hxx)0
-rw-r--r--src/input/plugins/DespotifyInputPlugin.cxx (renamed from src/input/DespotifyInputPlugin.cxx)4
-rw-r--r--src/input/plugins/DespotifyInputPlugin.hxx (renamed from src/input/DespotifyInputPlugin.hxx)0
-rw-r--r--src/input/plugins/FfmpegInputPlugin.cxx (renamed from src/input/FfmpegInputPlugin.cxx)4
-rw-r--r--src/input/plugins/FfmpegInputPlugin.hxx (renamed from src/input/FfmpegInputPlugin.hxx)0
-rw-r--r--src/input/plugins/FileInputPlugin.cxx (renamed from src/input/FileInputPlugin.cxx)4
-rw-r--r--src/input/plugins/FileInputPlugin.hxx (renamed from src/input/FileInputPlugin.hxx)0
-rw-r--r--src/input/plugins/MmsInputPlugin.cxx (renamed from src/input/MmsInputPlugin.cxx)4
-rw-r--r--src/input/plugins/MmsInputPlugin.hxx (renamed from src/input/MmsInputPlugin.hxx)0
-rw-r--r--src/input/plugins/RewindInputPlugin.cxx (renamed from src/input/RewindInputPlugin.cxx)4
-rw-r--r--src/input/plugins/RewindInputPlugin.hxx (renamed from src/input/RewindInputPlugin.hxx)0
-rw-r--r--src/input/plugins/SmbclientInputPlugin.cxx (renamed from src/input/SmbclientInputPlugin.cxx)4
-rw-r--r--src/input/plugins/SmbclientInputPlugin.hxx (renamed from src/input/SmbclientInputPlugin.hxx)0
29 files changed, 1027 insertions, 20 deletions
diff --git a/src/input/Init.cxx b/src/input/Init.cxx
new file mode 100644
index 000000000..14a7feef3
--- /dev/null
+++ b/src/input/Init.cxx
@@ -0,0 +1,102 @@
+/*
+ * 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 "Init.hxx"
+#include "Registry.hxx"
+#include "InputPlugin.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+#include "config/ConfigGlobal.hxx"
+#include "config/ConfigOption.hxx"
+#include "config/ConfigData.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+extern constexpr Domain input_domain("input");
+
+/**
+ * Find the "input" configuration block for the specified plugin.
+ *
+ * @param plugin_name the name of the input plugin
+ * @return the configuration block, or nullptr if none was configured
+ */
+static const struct config_param *
+input_plugin_config(const char *plugin_name, Error &error)
+{
+ const struct config_param *param = nullptr;
+
+ while ((param = config_get_next_param(CONF_INPUT, param)) != nullptr) {
+ const char *name = param->GetBlockValue("plugin");
+ if (name == nullptr) {
+ error.Format(input_domain,
+ "input configuration without 'plugin' name in line %d",
+ param->line);
+ return nullptr;
+ }
+
+ if (strcmp(name, plugin_name) == 0)
+ return param;
+ }
+
+ return nullptr;
+}
+
+bool
+input_stream_global_init(Error &error)
+{
+ const config_param empty;
+
+ for (unsigned i = 0; input_plugins[i] != nullptr; ++i) {
+ const InputPlugin *plugin = input_plugins[i];
+
+ assert(plugin->name != nullptr);
+ assert(*plugin->name != 0);
+ assert(plugin->open != nullptr);
+
+ const struct config_param *param =
+ input_plugin_config(plugin->name, error);
+ if (param == nullptr) {
+ if (error.IsDefined())
+ return false;
+
+ param = &empty;
+ } else if (!param->GetBlockValue("enabled", true))
+ /* the plugin is disabled in mpd.conf */
+ continue;
+
+ if (plugin->init == nullptr || plugin->init(*param, error))
+ input_plugins_enabled[i] = true;
+ else {
+ error.FormatPrefix("Failed to initialize input plugin '%s': ",
+ plugin->name);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void input_stream_global_finish(void)
+{
+ input_plugins_for_each_enabled(plugin)
+ if (plugin->finish != nullptr)
+ plugin->finish();
+}
diff --git a/src/input/Init.hxx b/src/input/Init.hxx
new file mode 100644
index 000000000..875fdce7c
--- /dev/null
+++ b/src/input/Init.hxx
@@ -0,0 +1,36 @@
+/*
+ * 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_INPUT_INIT_HXX
+#define MPD_INPUT_INIT_HXX
+
+class Error;
+
+/**
+ * Initializes this library and all input_stream implementations.
+ */
+bool
+input_stream_global_init(Error &error);
+
+/**
+ * Deinitializes this library and all input_stream implementations.
+ */
+void input_stream_global_finish(void);
+
+#endif
diff --git a/src/input/InputPlugin.hxx b/src/input/InputPlugin.hxx
new file mode 100644
index 000000000..83e0ab26e
--- /dev/null
+++ b/src/input/InputPlugin.hxx
@@ -0,0 +1,91 @@
+/*
+ * 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_INPUT_PLUGIN_HXX
+#define MPD_INPUT_PLUGIN_HXX
+
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct config_param;
+struct InputStream;
+class Error;
+struct Tag;
+
+struct InputPlugin {
+ typedef int64_t offset_type;
+
+ const char *name;
+
+ /**
+ * Global initialization. This method is called when MPD starts.
+ *
+ * @return true on success, false if the plugin should be
+ * disabled
+ */
+ bool (*init)(const config_param &param, Error &error);
+
+ /**
+ * Global deinitialization. Called once before MPD shuts
+ * down (only if init() has returned true).
+ */
+ void (*finish)(void);
+
+ InputStream *(*open)(const char *uri,
+ Mutex &mutex, Cond &cond,
+ Error &error);
+ void (*close)(InputStream *is);
+
+ /**
+ * Check for errors that may have occurred in the I/O thread.
+ * May be unimplemented for synchronous plugins.
+ *
+ * @return false on error
+ */
+ bool (*check)(InputStream *is, Error &error);
+
+ /**
+ * Update the public attributes. Call before access. Can be
+ * nullptr if the plugin always keeps its attributes up to date.
+ */
+ void (*update)(InputStream *is);
+
+ Tag *(*tag)(InputStream *is);
+
+ /**
+ * Returns true if the next read operation will not block:
+ * either data is available, or end-of-stream has been
+ * reached, or an error has occurred.
+ *
+ * If this method is unimplemented, then it is assumed that
+ * reading will never block.
+ */
+ bool (*available)(InputStream *is);
+
+ size_t (*read)(InputStream *is, void *ptr, size_t size,
+ Error &error);
+ bool (*eof)(InputStream *is);
+ bool (*seek)(InputStream *is, offset_type offset, int whence,
+ Error &error);
+};
+
+#endif
diff --git a/src/input/InputStream.cxx b/src/input/InputStream.cxx
new file mode 100644
index 000000000..0621437c4
--- /dev/null
+++ b/src/input/InputStream.cxx
@@ -0,0 +1,214 @@
+/*
+ * 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 "InputStream.hxx"
+#include "Registry.hxx"
+#include "InputPlugin.hxx"
+#include "plugins/RewindInputPlugin.hxx"
+#include "util/UriUtil.hxx"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+
+#include <assert.h>
+#include <stdio.h> /* for SEEK_SET */
+
+static constexpr Domain input_domain("input");
+
+InputStream *
+InputStream::Open(const char *url,
+ Mutex &mutex, Cond &cond,
+ Error &error)
+{
+ input_plugins_for_each_enabled(plugin) {
+ InputStream *is;
+
+ is = plugin->open(url, mutex, cond, error);
+ if (is != nullptr) {
+ assert(is->plugin.close != nullptr);
+ assert(is->plugin.read != nullptr);
+ assert(is->plugin.eof != nullptr);
+ assert(!is->seekable || is->plugin.seek != nullptr);
+
+ is = input_rewind_open(is);
+
+ return is;
+ } else if (error.IsDefined())
+ return nullptr;
+ }
+
+ error.Set(input_domain, "Unrecognized URI");
+ return nullptr;
+}
+
+InputStream *
+InputStream::OpenReady(const char *uri,
+ Mutex &mutex, Cond &cond,
+ Error &error)
+{
+ InputStream *is = Open(uri, mutex, cond, error);
+ if (is == nullptr)
+ return nullptr;
+
+ mutex.lock();
+ is->WaitReady();
+ bool success = is->Check(error);
+ mutex.unlock();
+
+ if (!success) {
+ is->Close();
+ is = nullptr;
+ }
+
+ return is;
+}
+
+bool
+InputStream::Check(Error &error)
+{
+ return plugin.check == nullptr || plugin.check(this, error);
+}
+
+void
+InputStream::Update()
+{
+ if (plugin.update != nullptr)
+ plugin.update(this);
+}
+
+void
+InputStream::WaitReady()
+{
+ while (true) {
+ Update();
+ if (ready)
+ break;
+
+ cond.wait(mutex);
+ }
+}
+
+void
+InputStream::LockWaitReady()
+{
+ const ScopeLock protect(mutex);
+ WaitReady();
+}
+
+bool
+InputStream::CheapSeeking() const
+{
+ return IsSeekable() && !uri_has_scheme(uri.c_str());
+}
+
+bool
+InputStream::Seek(offset_type _offset, int whence, Error &error)
+{
+ if (plugin.seek == nullptr)
+ return false;
+
+ return plugin.seek(this, _offset, whence, error);
+}
+
+bool
+InputStream::LockSeek(offset_type _offset, int whence, Error &error)
+{
+ if (plugin.seek == nullptr)
+ return false;
+
+ const ScopeLock protect(mutex);
+ return Seek(_offset, whence, error);
+}
+
+bool
+InputStream::Rewind(Error &error)
+{
+ return Seek(0, SEEK_SET, error);
+}
+
+bool
+InputStream::LockRewind(Error &error)
+{
+ return LockSeek(0, SEEK_SET, error);
+}
+
+Tag *
+InputStream::ReadTag()
+{
+ return plugin.tag != nullptr
+ ? plugin.tag(this)
+ : nullptr;
+}
+
+Tag *
+InputStream::LockReadTag()
+{
+ if (plugin.tag == nullptr)
+ return nullptr;
+
+ const ScopeLock protect(mutex);
+ return ReadTag();
+}
+
+bool
+InputStream::IsAvailable()
+{
+ return plugin.available != nullptr
+ ? plugin.available(this)
+ : true;
+}
+
+size_t
+InputStream::Read(void *ptr, size_t _size, Error &error)
+{
+ assert(ptr != nullptr);
+ assert(_size > 0);
+
+ return plugin.read(this, ptr, _size, error);
+}
+
+size_t
+InputStream::LockRead(void *ptr, size_t _size, Error &error)
+{
+ assert(ptr != nullptr);
+ assert(_size > 0);
+
+ const ScopeLock protect(mutex);
+ return Read(ptr, _size, error);
+}
+
+void
+InputStream::Close()
+{
+ plugin.close(this);
+}
+
+bool
+InputStream::IsEOF()
+{
+ return plugin.eof(this);
+}
+
+bool
+InputStream::LockIsEOF()
+{
+ const ScopeLock protect(mutex);
+ return IsEOF();
+}
+
diff --git a/src/input/InputStream.hxx b/src/input/InputStream.hxx
new file mode 100644
index 000000000..c66091687
--- /dev/null
+++ b/src/input/InputStream.hxx
@@ -0,0 +1,301 @@
+/*
+ * 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_INPUT_STREAM_HXX
+#define MPD_INPUT_STREAM_HXX
+
+#include "check.h"
+#include "thread/Mutex.hxx"
+#include "Compiler.h"
+
+#include <string>
+
+#include <assert.h>
+#include <stdint.h>
+
+class Cond;
+class Error;
+struct Tag;
+struct InputPlugin;
+
+struct InputStream {
+ typedef int64_t offset_type;
+
+ /**
+ * the plugin which implements this input stream
+ */
+ const InputPlugin &plugin;
+
+ /**
+ * The absolute URI which was used to open this stream.
+ */
+ std::string uri;
+
+ /**
+ * A mutex that protects the mutable attributes of this object
+ * and its implementation. It must be locked before calling
+ * any of the public methods.
+ *
+ * This object is allocated by the client, and the client is
+ * responsible for freeing it.
+ */
+ Mutex &mutex;
+
+ /**
+ * A cond that gets signalled when the state of this object
+ * changes from the I/O thread. The client of this object may
+ * wait on it. Optional, may be nullptr.
+ *
+ * This object is allocated by the client, and the client is
+ * responsible for freeing it.
+ */
+ Cond &cond;
+
+ /**
+ * indicates whether the stream is ready for reading and
+ * whether the other attributes in this struct are valid
+ */
+ bool ready;
+
+ /**
+ * if true, then the stream is fully seekable
+ */
+ bool seekable;
+
+ /**
+ * the size of the resource, or -1 if unknown
+ */
+ offset_type size;
+
+ /**
+ * the current offset within the stream
+ */
+ offset_type offset;
+
+ /**
+ * the MIME content type of the resource, or empty if unknown.
+ */
+ std::string mime;
+
+ InputStream(const InputPlugin &_plugin,
+ const char *_uri, Mutex &_mutex, Cond &_cond)
+ :plugin(_plugin), uri(_uri),
+ mutex(_mutex), cond(_cond),
+ ready(false), seekable(false),
+ size(-1), offset(0) {
+ assert(_uri != nullptr);
+ }
+
+ /**
+ * Opens a new input stream. You may not access it until the "ready"
+ * flag is set.
+ *
+ * @param mutex a mutex that is used to protect this object; must be
+ * locked before calling any of the public methods
+ * @param cond a cond that gets signalled when the state of
+ * this object changes; may be nullptr if the caller doesn't want to get
+ * notifications
+ * @return an #InputStream object on success, nullptr on error
+ */
+ gcc_nonnull_all
+ gcc_malloc
+ static InputStream *Open(const char *uri, Mutex &mutex, Cond &cond,
+ Error &error);
+
+ /**
+ * Just like Open(), but waits for the stream to become ready.
+ * It is a wrapper for Open(), WaitReady() and Check().
+ */
+ gcc_malloc gcc_nonnull_all
+ static InputStream *OpenReady(const char *uri,
+ Mutex &mutex, Cond &cond,
+ Error &error);
+
+ /**
+ * Close the input stream and free resources.
+ *
+ * The caller must not lock the mutex.
+ */
+ void Close();
+
+ void Lock() {
+ mutex.lock();
+ }
+
+ void Unlock() {
+ mutex.unlock();
+ }
+
+ /**
+ * Check for errors that may have occurred in the I/O thread.
+ *
+ * @return false on error
+ */
+ bool Check(Error &error);
+
+ /**
+ * Update the public attributes. Call before accessing attributes
+ * such as "ready" or "offset".
+ */
+ void Update();
+
+ /**
+ * Wait until the stream becomes ready.
+ *
+ * The caller must lock the mutex.
+ */
+ void WaitReady();
+
+ /**
+ * Wrapper for WaitReady() which locks and unlocks the mutex;
+ * the caller must not be holding it already.
+ */
+ void LockWaitReady();
+
+ gcc_pure
+ const char *GetMimeType() const {
+ assert(ready);
+
+ return mime.empty() ? nullptr : mime.c_str();
+ }
+
+ gcc_nonnull_all
+ void OverrideMimeType(const char *_mime) {
+ assert(ready);
+
+ mime = _mime;
+ }
+
+ gcc_pure
+ offset_type GetSize() const {
+ assert(ready);
+
+ return size;
+ }
+
+ gcc_pure
+ offset_type GetOffset() const {
+ assert(ready);
+
+ return offset;
+ }
+
+ gcc_pure
+ bool IsSeekable() const {
+ assert(ready);
+
+ return seekable;
+ }
+
+ /**
+ * Determines whether seeking is cheap. This is true for local files.
+ */
+ gcc_pure
+ bool CheapSeeking() const;
+
+ /**
+ * Seeks to the specified position in the stream. This will most
+ * likely fail if the "seekable" flag is false.
+ *
+ * The caller must lock the mutex.
+ *
+ * @param offset the relative offset
+ * @param whence the base of the seek, one of SEEK_SET, SEEK_CUR, SEEK_END
+ */
+ bool Seek(offset_type offset, int whence, Error &error);
+
+ /**
+ * Wrapper for Seek() which locks and unlocks the mutex; the
+ * caller must not be holding it already.
+ */
+ bool LockSeek(offset_type offset, int whence, Error &error);
+
+ /**
+ * Rewind to the beginning of the stream. This is a wrapper
+ * for Seek(0, SEEK_SET, error).
+ */
+ bool Rewind(Error &error);
+ bool LockRewind(Error &error);
+
+ /**
+ * Returns true if the stream has reached end-of-file.
+ *
+ * The caller must lock the mutex.
+ */
+ gcc_pure
+ bool IsEOF();
+
+ /**
+ * Wrapper for IsEOF() which locks and unlocks the mutex; the
+ * caller must not be holding it already.
+ */
+ gcc_pure
+ bool LockIsEOF();
+
+ /**
+ * Reads the tag from the stream.
+ *
+ * The caller must lock the mutex.
+ *
+ * @return a tag object which must be freed by the caller, or
+ * nullptr if the tag has not changed since the last call
+ */
+ gcc_malloc
+ Tag *ReadTag();
+
+ /**
+ * Wrapper for ReadTag() which locks and unlocks the mutex;
+ * the caller must not be holding it already.
+ */
+ gcc_malloc
+ Tag *LockReadTag();
+
+ /**
+ * Returns true if the next read operation will not block: either data
+ * is available, or end-of-stream has been reached, or an error has
+ * occurred.
+ *
+ * The caller must lock the mutex.
+ */
+ gcc_pure
+ bool IsAvailable();
+
+ /**
+ * Reads data from the stream into the caller-supplied buffer.
+ * Returns 0 on error or eof (check with IsEOF()).
+ *
+ * The caller must lock the mutex.
+ *
+ * @param is the InputStream object
+ * @param ptr the buffer to read into
+ * @param size the maximum number of bytes to read
+ * @return the number of bytes read
+ */
+ gcc_nonnull_all
+ size_t Read(void *ptr, size_t size, Error &error);
+
+ /**
+ * Wrapper for Read() which locks and unlocks the mutex;
+ * the caller must not be holding it already.
+ */
+ gcc_nonnull_all
+ size_t LockRead(void *ptr, size_t size, Error &error);
+};
+
+#endif
diff --git a/src/input/Registry.cxx b/src/input/Registry.cxx
new file mode 100644
index 000000000..30c6a67e0
--- /dev/null
+++ b/src/input/Registry.cxx
@@ -0,0 +1,86 @@
+/*
+ * 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 "Registry.hxx"
+#include "util/Macros.hxx"
+#include "plugins/FileInputPlugin.hxx"
+
+#ifdef HAVE_ALSA
+#include "plugins/AlsaInputPlugin.hxx"
+#endif
+
+#ifdef ENABLE_ARCHIVE
+#include "plugins/ArchiveInputPlugin.hxx"
+#endif
+
+#ifdef ENABLE_CURL
+#include "plugins/CurlInputPlugin.hxx"
+#endif
+
+#ifdef HAVE_FFMPEG
+#include "plugins/FfmpegInputPlugin.hxx"
+#endif
+
+#ifdef ENABLE_SMBCLIENT
+#include "plugins/SmbclientInputPlugin.hxx"
+#endif
+
+#ifdef ENABLE_MMS
+#include "plugins/MmsInputPlugin.hxx"
+#endif
+
+#ifdef ENABLE_CDIO_PARANOIA
+#include "plugins/CdioParanoiaInputPlugin.hxx"
+#endif
+
+#ifdef ENABLE_DESPOTIFY
+#include "plugins/DespotifyInputPlugin.hxx"
+#endif
+
+const InputPlugin *const input_plugins[] = {
+ &input_plugin_file,
+#ifdef HAVE_ALSA
+ &input_plugin_alsa,
+#endif
+#ifdef ENABLE_ARCHIVE
+ &input_plugin_archive,
+#endif
+#ifdef ENABLE_CURL
+ &input_plugin_curl,
+#endif
+#ifdef HAVE_FFMPEG
+ &input_plugin_ffmpeg,
+#endif
+#ifdef ENABLE_SMBCLIENT
+ &input_plugin_smbclient,
+#endif
+#ifdef ENABLE_MMS
+ &input_plugin_mms,
+#endif
+#ifdef ENABLE_CDIO_PARANOIA
+ &input_plugin_cdio_paranoia,
+#endif
+#ifdef ENABLE_DESPOTIFY
+ &input_plugin_despotify,
+#endif
+ nullptr
+};
+
+bool input_plugins_enabled[ARRAY_SIZE(input_plugins) - 1];
diff --git a/src/input/Registry.hxx b/src/input/Registry.hxx
new file mode 100644
index 000000000..1b81f8f06
--- /dev/null
+++ b/src/input/Registry.hxx
@@ -0,0 +1,43 @@
+/*
+ * 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_INPUT_REGISTRY_HXX
+#define MPD_INPUT_REGISTRY_HXX
+
+#include "check.h"
+
+/**
+ * NULL terminated list of all input plugins which were enabled at
+ * compile time.
+ */
+extern const struct InputPlugin *const input_plugins[];
+
+extern bool input_plugins_enabled[];
+
+#define input_plugins_for_each(plugin) \
+ for (const InputPlugin *plugin, \
+ *const*input_plugin_iterator = &input_plugins[0]; \
+ (plugin = *input_plugin_iterator) != NULL; \
+ ++input_plugin_iterator)
+
+#define input_plugins_for_each_enabled(plugin) \
+ input_plugins_for_each(plugin) \
+ if (input_plugins_enabled[input_plugin_iterator - input_plugins])
+
+#endif
diff --git a/src/input/TextInputStream.cxx b/src/input/TextInputStream.cxx
new file mode 100644
index 000000000..25b9b42fe
--- /dev/null
+++ b/src/input/TextInputStream.cxx
@@ -0,0 +1,78 @@
+/*
+ * 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 "TextInputStream.hxx"
+#include "InputStream.hxx"
+#include "util/CharUtil.hxx"
+#include "util/Error.hxx"
+#include "Log.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+bool TextInputStream::ReadLine(std::string &line)
+{
+ const char *src, *p;
+
+ do {
+ size_t nbytes;
+ auto dest = buffer.Write();
+ if (dest.size >= 2) {
+ /* reserve one byte for the null terminator if
+ the last line is not terminated by a
+ newline character */
+ --dest.size;
+
+ Error error;
+ nbytes = is.LockRead(dest.data, dest.size, error);
+ if (nbytes > 0)
+ buffer.Append(nbytes);
+ else if (error.IsDefined()) {
+ LogError(error);
+ return false;
+ }
+ } else
+ nbytes = 0;
+
+ auto src_p = buffer.Read();
+ if (src_p.IsEmpty())
+ return false;
+
+ src = src_p.data;
+
+ p = reinterpret_cast<const char*>(memchr(src, '\n', src_p.size));
+ if (p == nullptr && nbytes == 0) {
+ /* end of file (or line too long): terminate
+ the current line */
+ dest = buffer.Write();
+ assert(!dest.IsEmpty());
+ dest.data[0] = '\n';
+ buffer.Append(1);
+ }
+ } while (p == nullptr);
+
+ size_t length = p - src + 1;
+ while (p > src && IsWhitespaceOrNull(p[-1]))
+ --p;
+
+ line = std::string(src, p - src);
+ buffer.Consume(length);
+ return true;
+}
diff --git a/src/input/TextInputStream.hxx b/src/input/TextInputStream.hxx
new file mode 100644
index 000000000..86ae16e41
--- /dev/null
+++ b/src/input/TextInputStream.hxx
@@ -0,0 +1,56 @@
+/*
+ * 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_TEXT_INPUT_STREAM_HXX
+#define MPD_TEXT_INPUT_STREAM_HXX
+
+#include "util/FifoBuffer.hxx"
+
+#include <string>
+
+struct InputStream;
+
+class TextInputStream {
+ InputStream &is;
+ FifoBuffer<char, 4096> buffer;
+
+public:
+ /**
+ * Wraps an existing #input_stream object into a #TextInputStream,
+ * to read its contents as text lines.
+ *
+ * @param _is an open #input_stream object
+ */
+ explicit TextInputStream(InputStream &_is)
+ :is(_is) {}
+
+ TextInputStream(const TextInputStream &) = delete;
+ TextInputStream& operator=(const TextInputStream &) = delete;
+
+ /**
+ * Reads the next line from the stream with newline character stripped.
+ *
+ * @param line a string to put result to
+ * @return true if line is read successfully, false on end of file
+ * or error
+ */
+ bool ReadLine(std::string &line);
+};
+
+#endif
diff --git a/src/input/AlsaInputPlugin.cxx b/src/input/plugins/AlsaInputPlugin.cxx
index 29c6b2504..da2d9a926 100644
--- a/src/input/AlsaInputPlugin.cxx
+++ b/src/input/plugins/AlsaInputPlugin.cxx
@@ -26,8 +26,8 @@
#include "config.h"
#include "AlsaInputPlugin.hxx"
-#include "InputPlugin.hxx"
-#include "InputStream.hxx"
+#include "../InputPlugin.hxx"
+#include "../InputStream.hxx"
#include "util/Domain.hxx"
#include "util/Error.hxx"
#include "util/StringUtil.hxx"
diff --git a/src/input/AlsaInputPlugin.hxx b/src/input/plugins/AlsaInputPlugin.hxx
index dda1e5176..dddf7dfd7 100644
--- a/src/input/AlsaInputPlugin.hxx
+++ b/src/input/plugins/AlsaInputPlugin.hxx
@@ -20,7 +20,7 @@
#ifndef MPD_ALSA_INPUT_PLUGIN_HXX
#define MPD_ALSA_INPUT_PLUGIN_HXX
-#include "InputPlugin.hxx"
+#include "../InputPlugin.hxx"
extern const struct InputPlugin input_plugin_alsa;
diff --git a/src/input/ArchiveInputPlugin.cxx b/src/input/plugins/ArchiveInputPlugin.cxx
index e5591a95e..df1a72585 100644
--- a/src/input/ArchiveInputPlugin.cxx
+++ b/src/input/plugins/ArchiveInputPlugin.cxx
@@ -24,7 +24,7 @@
#include "archive/ArchiveList.hxx"
#include "archive/ArchivePlugin.hxx"
#include "archive/ArchiveFile.hxx"
-#include "InputPlugin.hxx"
+#include "../InputPlugin.hxx"
#include "fs/Traits.hxx"
#include "util/Alloc.hxx"
#include "Log.hxx"
diff --git a/src/input/ArchiveInputPlugin.hxx b/src/input/plugins/ArchiveInputPlugin.hxx
index 024723726..024723726 100644
--- a/src/input/ArchiveInputPlugin.hxx
+++ b/src/input/plugins/ArchiveInputPlugin.hxx
diff --git a/src/input/CdioParanoiaInputPlugin.cxx b/src/input/plugins/CdioParanoiaInputPlugin.cxx
index 297ee84dc..9267b50e1 100644
--- a/src/input/CdioParanoiaInputPlugin.cxx
+++ b/src/input/plugins/CdioParanoiaInputPlugin.cxx
@@ -23,8 +23,8 @@
#include "config.h"
#include "CdioParanoiaInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
diff --git a/src/input/CdioParanoiaInputPlugin.hxx b/src/input/plugins/CdioParanoiaInputPlugin.hxx
index e2804e8c7..e2804e8c7 100644
--- a/src/input/CdioParanoiaInputPlugin.hxx
+++ b/src/input/plugins/CdioParanoiaInputPlugin.hxx
diff --git a/src/input/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx
index 64321b38d..a3c6c1d1a 100644
--- a/src/input/CurlInputPlugin.cxx
+++ b/src/input/plugins/CurlInputPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "CurlInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/ConfigData.hxx"
#include "tag/Tag.hxx"
diff --git a/src/input/CurlInputPlugin.hxx b/src/input/plugins/CurlInputPlugin.hxx
index 4acb18bfc..4acb18bfc 100644
--- a/src/input/CurlInputPlugin.hxx
+++ b/src/input/plugins/CurlInputPlugin.hxx
diff --git a/src/input/DespotifyInputPlugin.cxx b/src/input/plugins/DespotifyInputPlugin.cxx
index 29a80e585..a01ba3759 100644
--- a/src/input/DespotifyInputPlugin.cxx
+++ b/src/input/plugins/DespotifyInputPlugin.cxx
@@ -20,8 +20,8 @@
#include "config.h"
#include "DespotifyInputPlugin.hxx"
#include "DespotifyUtils.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "tag/Tag.hxx"
#include "util/StringUtil.hxx"
#include "Log.hxx"
diff --git a/src/input/DespotifyInputPlugin.hxx b/src/input/plugins/DespotifyInputPlugin.hxx
index 83f963520..83f963520 100644
--- a/src/input/DespotifyInputPlugin.hxx
+++ b/src/input/plugins/DespotifyInputPlugin.hxx
diff --git a/src/input/FfmpegInputPlugin.cxx b/src/input/plugins/FfmpegInputPlugin.cxx
index 50fec8aec..0d0a4375e 100644
--- a/src/input/FfmpegInputPlugin.cxx
+++ b/src/input/plugins/FfmpegInputPlugin.cxx
@@ -22,8 +22,8 @@
#include "config.h"
#include "FfmpegInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
diff --git a/src/input/FfmpegInputPlugin.hxx b/src/input/plugins/FfmpegInputPlugin.hxx
index 43f829e89..43f829e89 100644
--- a/src/input/FfmpegInputPlugin.hxx
+++ b/src/input/plugins/FfmpegInputPlugin.hxx
diff --git a/src/input/FileInputPlugin.cxx b/src/input/plugins/FileInputPlugin.cxx
index d9f5a217f..780e93263 100644
--- a/src/input/FileInputPlugin.cxx
+++ b/src/input/plugins/FileInputPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h" /* must be first for large file support */
#include "FileInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "fs/Traits.hxx"
diff --git a/src/input/FileInputPlugin.hxx b/src/input/plugins/FileInputPlugin.hxx
index 4aef94637..4aef94637 100644
--- a/src/input/FileInputPlugin.hxx
+++ b/src/input/plugins/FileInputPlugin.hxx
diff --git a/src/input/MmsInputPlugin.cxx b/src/input/plugins/MmsInputPlugin.cxx
index 8f946ae5e..a7068cb2b 100644
--- a/src/input/MmsInputPlugin.cxx
+++ b/src/input/plugins/MmsInputPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "MmsInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
diff --git a/src/input/MmsInputPlugin.hxx b/src/input/plugins/MmsInputPlugin.hxx
index b4017ffd6..b4017ffd6 100644
--- a/src/input/MmsInputPlugin.hxx
+++ b/src/input/plugins/MmsInputPlugin.hxx
diff --git a/src/input/RewindInputPlugin.cxx b/src/input/plugins/RewindInputPlugin.cxx
index 685ca1e6e..1a930ac53 100644
--- a/src/input/RewindInputPlugin.cxx
+++ b/src/input/plugins/RewindInputPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "RewindInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include <assert.h>
#include <string.h>
diff --git a/src/input/RewindInputPlugin.hxx b/src/input/plugins/RewindInputPlugin.hxx
index f19705154..f19705154 100644
--- a/src/input/RewindInputPlugin.hxx
+++ b/src/input/plugins/RewindInputPlugin.hxx
diff --git a/src/input/SmbclientInputPlugin.cxx b/src/input/plugins/SmbclientInputPlugin.cxx
index ed94d581a..9fbe72b59 100644
--- a/src/input/SmbclientInputPlugin.cxx
+++ b/src/input/plugins/SmbclientInputPlugin.cxx
@@ -19,8 +19,8 @@
#include "config.h"
#include "SmbclientInputPlugin.hxx"
-#include "InputStream.hxx"
-#include "InputPlugin.hxx"
+#include "../InputStream.hxx"
+#include "../InputPlugin.hxx"
#include "util/StringUtil.hxx"
#include "util/Error.hxx"
diff --git a/src/input/SmbclientInputPlugin.hxx b/src/input/plugins/SmbclientInputPlugin.hxx
index a0539d020..a0539d020 100644
--- a/src/input/SmbclientInputPlugin.hxx
+++ b/src/input/plugins/SmbclientInputPlugin.hxx