input/Plugin: migrate open() from class Error to C++ exceptions
This commit is contained in:
		| @@ -28,26 +28,28 @@ | ||||
| bool | ||||
| tag_archive_scan(ArchiveFile &archive, const char *path_utf8, | ||||
| 		 const TagHandler &handler, void *handler_ctx) | ||||
| { | ||||
| try { | ||||
| 	Mutex mutex; | ||||
| 	Cond cond; | ||||
|  | ||||
| 	InputStreamPtr is(archive.OpenStream(path_utf8, mutex, cond, | ||||
| 					     IgnoreError())); | ||||
| 	InputStreamPtr is(archive.OpenStream(path_utf8, mutex, cond)); | ||||
| 	if (!is) | ||||
| 		return false; | ||||
|  | ||||
| 	return tag_stream_scan(*is, handler, handler_ctx); | ||||
| } catch (const std::exception &e) { | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool | ||||
| tag_archive_scan(ArchiveFile &archive, const char *path_utf8, | ||||
| 		 TagBuilder &builder) | ||||
| { | ||||
| try { | ||||
| 	Mutex mutex; | ||||
| 	Cond cond; | ||||
|  | ||||
| 	InputStreamPtr is(archive.OpenStream(path_utf8, mutex, cond, | ||||
| 					     IgnoreError())); | ||||
| 	InputStreamPtr is(archive.OpenStream(path_utf8, mutex, cond)); | ||||
| 	return is && tag_stream_scan(*is, builder); | ||||
| } catch (const std::exception &e) { | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,8 @@ | ||||
| #include "input/LocalOpen.hxx" | ||||
| #include "thread/Cond.hxx" | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| class TagFileScan { | ||||
| @@ -60,11 +62,12 @@ public: | ||||
|  | ||||
| 		/* open the InputStream (if not already open) */ | ||||
| 		if (is == nullptr) { | ||||
| 			is = OpenLocalInputStream(path_fs, | ||||
| 						  mutex, cond, | ||||
| 						  IgnoreError()); | ||||
| 			if (is == nullptr) | ||||
| 			try { | ||||
| 				is = OpenLocalInputStream(path_fs, | ||||
| 							  mutex, cond); | ||||
| 			} catch (const std::runtime_error &) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} else | ||||
| 			is->LockRewind(IgnoreError()); | ||||
|  | ||||
|   | ||||
| @@ -72,13 +72,14 @@ tag_stream_scan(InputStream &is, const TagHandler &handler, void *ctx) | ||||
|  | ||||
| bool | ||||
| tag_stream_scan(const char *uri, const TagHandler &handler, void *ctx) | ||||
| { | ||||
| try { | ||||
| 	Mutex mutex; | ||||
| 	Cond cond; | ||||
|  | ||||
| 	auto is = InputStream::OpenReady(uri, mutex, cond, | ||||
| 					 IgnoreError()); | ||||
| 	return is && tag_stream_scan(*is, handler, ctx); | ||||
| 	auto is = InputStream::OpenReady(uri, mutex, cond); | ||||
| 	return tag_stream_scan(*is, handler, ctx); | ||||
| } catch (const std::exception &e) { | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool | ||||
| @@ -97,11 +98,12 @@ tag_stream_scan(InputStream &is, TagBuilder &builder) | ||||
|  | ||||
| bool | ||||
| tag_stream_scan(const char *uri, TagBuilder &builder) | ||||
| { | ||||
| try { | ||||
| 	Mutex mutex; | ||||
| 	Cond cond; | ||||
|  | ||||
| 	auto is = InputStream::OpenReady(uri, mutex, cond, | ||||
| 					 IgnoreError()); | ||||
| 	return is && tag_stream_scan(*is, builder); | ||||
| 	auto is = InputStream::OpenReady(uri, mutex, cond); | ||||
| 	return tag_stream_scan(*is, builder); | ||||
| } catch (const std::exception &e) { | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -51,11 +51,12 @@ public: | ||||
| 	/** | ||||
| 	 * Opens an InputStream of a file within the archive. | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @param path the path within the archive | ||||
| 	 */ | ||||
| 	virtual InputStream *OpenStream(const char *path, | ||||
| 					Mutex &mutex, Cond &cond, | ||||
| 					Error &error) = 0; | ||||
| 					Mutex &mutex, Cond &cond) = 0; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -36,6 +36,8 @@ | ||||
|  | ||||
| #include <bzlib.h> | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| #include <stddef.h> | ||||
|  | ||||
| class Bzip2ArchiveFile final : public ArchiveFile { | ||||
| @@ -74,9 +76,8 @@ public: | ||||
| 		visitor.VisitArchiveEntry(name.c_str()); | ||||
| 	} | ||||
|  | ||||
| 	virtual InputStream *OpenStream(const char *path, | ||||
| 					Mutex &mutex, Cond &cond, | ||||
| 					Error &error) override; | ||||
| 	InputStream *OpenStream(const char *path, | ||||
| 				Mutex &mutex, Cond &cond) override; | ||||
| }; | ||||
|  | ||||
| class Bzip2InputStream final : public InputStream { | ||||
| @@ -93,13 +94,12 @@ public: | ||||
| 			 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; | ||||
|  | ||||
| private: | ||||
| 	void Open(); | ||||
| 	bool FillBuffer(Error &error); | ||||
| }; | ||||
|  | ||||
| @@ -107,8 +107,8 @@ static constexpr Domain bz2_domain("bz2"); | ||||
|  | ||||
| /* single archive handling allocation helpers */ | ||||
|  | ||||
| inline bool | ||||
| Bzip2InputStream::Open(Error &error) | ||||
| inline void | ||||
| Bzip2InputStream::Open() | ||||
| { | ||||
| 	bzstream.bzalloc = nullptr; | ||||
| 	bzstream.bzfree = nullptr; | ||||
| @@ -118,27 +118,20 @@ Bzip2InputStream::Open(Error &error) | ||||
| 	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; | ||||
| 	} | ||||
| 	if (ret != BZ_OK) | ||||
| 		throw std::runtime_error("BZ2_bzDecompressInit() has failed"); | ||||
|  | ||||
| 	SetReady(); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /* archive open && listing routine */ | ||||
|  | ||||
| static ArchiveFile * | ||||
| bz2_open(Path pathname, Error &error) | ||||
| bz2_open(Path pathname, gcc_unused Error &error) | ||||
| { | ||||
| 	static Mutex mutex; | ||||
| 	static Cond cond; | ||||
| 	auto is = OpenLocalInputStream(pathname, mutex, cond, error); | ||||
| 	if (is == nullptr) | ||||
| 		return nullptr; | ||||
|  | ||||
| 	auto is = OpenLocalInputStream(pathname, mutex, cond); | ||||
| 	return new Bzip2ArchiveFile(pathname, std::move(is)); | ||||
| } | ||||
|  | ||||
| @@ -150,6 +143,7 @@ Bzip2InputStream::Bzip2InputStream(Bzip2ArchiveFile &_context, | ||||
| 	:InputStream(_uri, _mutex, _cond), | ||||
| 	 archive(&_context) | ||||
| { | ||||
| 	Open(); | ||||
| 	archive->Ref(); | ||||
| } | ||||
|  | ||||
| @@ -161,16 +155,9 @@ Bzip2InputStream::~Bzip2InputStream() | ||||
|  | ||||
| InputStream * | ||||
| Bzip2ArchiveFile::OpenStream(const char *path, | ||||
| 			     Mutex &mutex, Cond &cond, | ||||
| 			     Error &error) | ||||
| 			     Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	Bzip2InputStream *bis = new Bzip2InputStream(*this, path, mutex, cond); | ||||
| 	if (!bis->Open(error)) { | ||||
| 		delete bis; | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	return bis; | ||||
| 	return new Bzip2InputStream(*this, path, mutex, cond); | ||||
| } | ||||
|  | ||||
| inline bool | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| #include "input/InputStream.hxx" | ||||
| #include "fs/Path.hxx" | ||||
| #include "util/RefCount.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/Domain.hxx" | ||||
|  | ||||
| @@ -77,9 +78,8 @@ public: | ||||
|  | ||||
| 	virtual void Visit(ArchiveVisitor &visitor) override; | ||||
|  | ||||
| 	virtual InputStream *OpenStream(const char *path, | ||||
| 					Mutex &mutex, Cond &cond, | ||||
| 					Error &error) override; | ||||
| 	InputStream *OpenStream(const char *path, | ||||
| 				Mutex &mutex, Cond &cond) override; | ||||
| }; | ||||
|  | ||||
| static constexpr Domain iso9660_domain("iso9660"); | ||||
| @@ -175,15 +175,12 @@ public: | ||||
|  | ||||
| InputStream * | ||||
| Iso9660ArchiveFile::OpenStream(const char *pathname, | ||||
| 			       Mutex &mutex, Cond &cond, | ||||
| 			       Error &error) | ||||
| 			       Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	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; | ||||
| 	} | ||||
| 	if (statbuf == nullptr) | ||||
| 		throw FormatRuntimeError("not found in the ISO file: %s", | ||||
| 					 pathname); | ||||
|  | ||||
| 	return new Iso9660InputStream(*this, pathname, mutex, cond, | ||||
| 				      statbuf); | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| #include "input/InputStream.hxx" | ||||
| #include "fs/Path.hxx" | ||||
| #include "util/RefCount.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/Domain.hxx" | ||||
|  | ||||
| @@ -58,9 +59,8 @@ public: | ||||
|  | ||||
| 	virtual void Visit(ArchiveVisitor &visitor) override; | ||||
|  | ||||
| 	virtual InputStream *OpenStream(const char *path, | ||||
| 					Mutex &mutex, Cond &cond, | ||||
| 					Error &error) override; | ||||
| 	InputStream *OpenStream(const char *path, | ||||
| 				Mutex &mutex, Cond &cond) override; | ||||
| }; | ||||
|  | ||||
| static constexpr Domain zzip_domain("zzip"); | ||||
| @@ -129,15 +129,12 @@ struct ZzipInputStream final : public InputStream { | ||||
|  | ||||
| InputStream * | ||||
| ZzipArchiveFile::OpenStream(const char *pathname, | ||||
| 			    Mutex &mutex, Cond &cond, | ||||
| 			    Error &error) | ||||
| 			    Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	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; | ||||
| 	} | ||||
| 	if (_file == nullptr) | ||||
| 		throw FormatRuntimeError("not found in the ZIP file: %s", | ||||
| 					 pathname); | ||||
|  | ||||
| 	return new ZzipInputStream(*this, pathname, | ||||
| 				   mutex, cond, | ||||
|   | ||||
| @@ -258,7 +258,7 @@ void decoder_seek_error(Decoder & decoder) | ||||
| } | ||||
|  | ||||
| InputStreamPtr | ||||
| decoder_open_uri(Decoder &decoder, const char *uri, Error &error) | ||||
| decoder_open_uri(Decoder &decoder, const char *uri) | ||||
| { | ||||
| 	assert(decoder.dc.state == DecoderState::START || | ||||
| 	       decoder.dc.state == DecoderState::DECODE); | ||||
| @@ -267,9 +267,7 @@ decoder_open_uri(Decoder &decoder, const char *uri, Error &error) | ||||
| 	Mutex &mutex = dc.mutex; | ||||
| 	Cond &cond = dc.cond; | ||||
|  | ||||
| 	auto is = InputStream::Open(uri, mutex, cond, error); | ||||
| 	if (!is) | ||||
| 		return nullptr; | ||||
| 	auto is = InputStream::Open(uri, mutex, cond); | ||||
|  | ||||
| 	const ScopeLock lock(mutex); | ||||
| 	while (true) { | ||||
|   | ||||
| @@ -121,11 +121,12 @@ decoder_seek_error(Decoder &decoder); | ||||
|  | ||||
| /** | ||||
|  * Open a new #InputStream and wait until it's ready.  Can get | ||||
|  * cancelled by DecoderCommand::STOP (returns nullptr without setting | ||||
|  * #Error). | ||||
|  * cancelled by DecoderCommand::STOP (returns nullptr). | ||||
|  * | ||||
|  * Throws std::runtime_error on error. | ||||
|  */ | ||||
| InputStreamPtr | ||||
| decoder_open_uri(Decoder &decoder, const char *uri, Error &error); | ||||
| decoder_open_uri(Decoder &decoder, const char *uri); | ||||
|  | ||||
| /** | ||||
|  * Blocking read from the input stream. | ||||
|   | ||||
| @@ -56,10 +56,7 @@ static constexpr Domain decoder_thread_domain("decoder_thread"); | ||||
| static InputStreamPtr | ||||
| decoder_input_stream_open(DecoderControl &dc, const char *uri) | ||||
| { | ||||
| 	Error error; | ||||
| 	auto is = InputStream::Open(uri, dc.mutex, dc.cond, error); | ||||
| 	if (is == nullptr) | ||||
| 		throw error; | ||||
| 	auto is = InputStream::Open(uri, dc.mutex, dc.cond); | ||||
|  | ||||
| 	/* wait for the input stream to become ready; its metadata | ||||
| 	   will be available then */ | ||||
| @@ -76,6 +73,7 @@ decoder_input_stream_open(DecoderControl &dc, const char *uri) | ||||
| 		is->Update(); | ||||
| 	} | ||||
|  | ||||
| 	Error error; | ||||
| 	if (!is->Check(error)) | ||||
| 		throw error; | ||||
|  | ||||
| @@ -85,10 +83,7 @@ decoder_input_stream_open(DecoderControl &dc, const char *uri) | ||||
| static InputStreamPtr | ||||
| decoder_input_stream_open(DecoderControl &dc, Path path) | ||||
| { | ||||
| 	Error error; | ||||
| 	auto is = OpenLocalInputStream(path, dc.mutex, dc.cond, error); | ||||
| 	if (is == nullptr) | ||||
| 		throw error; | ||||
| 	auto is = OpenLocalInputStream(path, dc.mutex, dc.cond); | ||||
|  | ||||
| 	assert(is->IsReady()); | ||||
|  | ||||
|   | ||||
| @@ -34,6 +34,8 @@ | ||||
|  | ||||
| #include <wavpack/wavpack.h> | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| @@ -490,7 +492,11 @@ wavpack_open_wvc(Decoder &decoder, const char *uri) | ||||
| 		free(wvc_url); | ||||
| 	}; | ||||
|  | ||||
| 	return decoder_open_uri(decoder, uri, IgnoreError()); | ||||
| 	try { | ||||
| 		return decoder_open_uri(decoder, uri); | ||||
| 	} catch (const std::runtime_error &) { | ||||
| 		return nullptr; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
|   | ||||
| @@ -81,6 +81,7 @@ AsyncInputStream::Resume() | ||||
|  | ||||
| 	if (paused) { | ||||
| 		paused = false; | ||||
|  | ||||
| 		DoResume(); | ||||
| 	} | ||||
| } | ||||
| @@ -264,7 +265,12 @@ AsyncInputStream::DeferredResume() | ||||
| { | ||||
| 	const ScopeLock protect(mutex); | ||||
|  | ||||
| 	Resume(); | ||||
| 	try { | ||||
| 		Resume(); | ||||
| 	} catch (...) { | ||||
| 		postponed_exception = std::current_exception(); | ||||
| 		cond.broadcast(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -274,10 +280,16 @@ AsyncInputStream::DeferredSeek() | ||||
| 	if (seek_state != SeekState::SCHEDULED) | ||||
| 		return; | ||||
|  | ||||
| 	Resume(); | ||||
| 	try { | ||||
| 		Resume(); | ||||
|  | ||||
| 	seek_state = SeekState::PENDING; | ||||
| 	buffer.Clear(); | ||||
| 	paused = false; | ||||
| 	DoSeek(seek_offset); | ||||
| 		seek_state = SeekState::PENDING; | ||||
| 		buffer.Clear(); | ||||
| 		paused = false; | ||||
|  | ||||
| 		DoSeek(seek_offset); | ||||
| 	} catch (...) { | ||||
| 		postponed_exception = std::current_exception(); | ||||
| 		cond.broadcast(); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -36,7 +36,6 @@ | ||||
|  | ||||
| struct ConfigBlock; | ||||
| class InputStream; | ||||
| class Error; | ||||
| struct Tag; | ||||
|  | ||||
| struct InputPlugin { | ||||
| @@ -58,9 +57,11 @@ struct InputPlugin { | ||||
| 	 */ | ||||
| 	void (*finish)(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 */ | ||||
| 	InputStream *(*open)(const char *uri, | ||||
| 			     Mutex &mutex, Cond &cond, | ||||
| 			     Error &error); | ||||
| 			     Mutex &mutex, Cond &cond); | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -115,16 +115,17 @@ public: | ||||
| 	 * Opens a new input stream.  You may not access it until the "ready" | ||||
| 	 * flag is set. | ||||
| 	 * | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 * | ||||
| 	 * @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 | ||||
| 	 * @return an #InputStream object on success | ||||
| 	 */ | ||||
| 	gcc_nonnull_all | ||||
| 	static InputStreamPtr Open(const char *uri, Mutex &mutex, Cond &cond, | ||||
| 				   Error &error); | ||||
| 	static InputStreamPtr Open(const char *uri, Mutex &mutex, Cond &cond); | ||||
|  | ||||
| 	/** | ||||
| 	 * Just like Open(), but waits for the stream to become ready. | ||||
| @@ -132,8 +133,7 @@ public: | ||||
| 	 */ | ||||
| 	gcc_nonnull_all | ||||
| 	static InputStreamPtr OpenReady(const char *uri, | ||||
| 					Mutex &mutex, Cond &cond, | ||||
| 					Error &error); | ||||
| 					Mutex &mutex, Cond &cond); | ||||
|  | ||||
| 	/** | ||||
| 	 * The absolute URI which was used to open this stream. | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
| #endif | ||||
|  | ||||
| #include "fs/Path.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "system/Error.hxx" | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| @@ -36,20 +36,24 @@ | ||||
| #endif | ||||
|  | ||||
| InputStreamPtr | ||||
| OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond, Error &error) | ||||
| OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	assert(!error.IsDefined()); | ||||
| 	InputStreamPtr is; | ||||
|  | ||||
| 	InputStreamPtr is(OpenFileInputStream(path, mutex, cond, error)); | ||||
| #ifdef ENABLE_ARCHIVE | ||||
| 	if (is == nullptr && error.IsDomain(errno_domain) && | ||||
| 	    error.GetCode() == ENOTDIR) { | ||||
| 		/* ENOTDIR means this may be a path inside an archive | ||||
| 		   file */ | ||||
| 		Error error2; | ||||
| 		is.reset(OpenArchiveInputStream(path, mutex, cond, error2)); | ||||
| 		if (is == nullptr && error2.IsDefined()) | ||||
| 			error = std::move(error2); | ||||
| 	try { | ||||
| #endif | ||||
| 		is = OpenFileInputStream(path, mutex, cond); | ||||
| #ifdef ENABLE_ARCHIVE | ||||
| 	} catch (const std::system_error &e) { | ||||
| 		if (IsPathNotFound(e)) { | ||||
| 			/* ENOTDIR means this may be a path inside an archive | ||||
| 			   file */ | ||||
| 			is = OpenArchiveInputStream(path, mutex, cond); | ||||
| 			if (!is) | ||||
| 				throw; | ||||
| 		} else | ||||
| 			throw; | ||||
| 	} | ||||
| #endif | ||||
|  | ||||
|   | ||||
| @@ -26,13 +26,14 @@ | ||||
| class Path; | ||||
| class Mutex; | ||||
| class Cond; | ||||
| class Error; | ||||
|  | ||||
| /** | ||||
|  * Open a "local" file.  This is a wrapper for the input plugins | ||||
|  * "file" and "archive". | ||||
|  * | ||||
|  * Throws std::runtime_error on error. | ||||
|  */ | ||||
| InputStreamPtr | ||||
| OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond, Error &error); | ||||
| OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -28,51 +28,51 @@ | ||||
| #include "fs/AllocatedPath.hxx" | ||||
| #include "util/Error.hxx" | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| InputStreamPtr | ||||
| InputStream::Open(const char *url, | ||||
| 		  Mutex &mutex, Cond &cond, | ||||
| 		  Error &error) | ||||
| 		  Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	if (PathTraitsUTF8::IsAbsolute(url)) { | ||||
| 		Error error; | ||||
| 		const auto path = AllocatedPath::FromUTF8(url, error); | ||||
| 		if (path.IsNull()) | ||||
| 			return nullptr; | ||||
| 			throw std::runtime_error(error.GetMessage()); | ||||
|  | ||||
| 		return OpenLocalInputStream(path, | ||||
| 					    mutex, cond, error); | ||||
| 		return OpenLocalInputStream(path, mutex, cond); | ||||
| 	} | ||||
|  | ||||
| 	input_plugins_for_each_enabled(plugin) { | ||||
| 		InputStream *is; | ||||
|  | ||||
| 		is = plugin->open(url, mutex, cond, error); | ||||
| 		is = plugin->open(url, mutex, cond); | ||||
| 		if (is != nullptr) { | ||||
| 			is = input_rewind_open(is); | ||||
|  | ||||
| 			return InputStreamPtr(is); | ||||
| 		} else if (error.IsDefined()) | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	error.Set(input_domain, "Unrecognized URI"); | ||||
| 	return nullptr; | ||||
| 	throw std::runtime_error("Unrecognized URI"); | ||||
| } | ||||
|  | ||||
| InputStreamPtr | ||||
| InputStream::OpenReady(const char *uri, | ||||
| 		       Mutex &mutex, Cond &cond, | ||||
| 		       Error &error) | ||||
| 		       Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	auto is = Open(uri, mutex, cond, error); | ||||
| 	if (is == nullptr) | ||||
| 		return nullptr; | ||||
| 	auto is = Open(uri, mutex, cond); | ||||
|  | ||||
| 	bool success; | ||||
|  | ||||
| 	{ | ||||
| 		const ScopeLock protect(mutex); | ||||
| 		is->WaitReady(); | ||||
|  | ||||
| 		Error error; | ||||
| 		success = is->Check(error); | ||||
| 		if (!success) | ||||
| 			throw std::runtime_error(error.GetMessage()); | ||||
| 	} | ||||
|  | ||||
| 	if (!success) | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| #include "../AsyncInputStream.hxx" | ||||
| #include "util/Domain.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
| #include "util/StringCompare.hxx" | ||||
| #include "util/ReusableArray.hxx" | ||||
| #include "util/ScopeExit.hxx" | ||||
| @@ -102,8 +103,7 @@ public: | ||||
| 		snd_pcm_close(capture_handle); | ||||
| 	} | ||||
|  | ||||
| 	static InputStream *Create(const char *uri, Mutex &mutex, Cond &cond, | ||||
| 				   Error &error); | ||||
| 	static InputStream *Create(const char *uri, Mutex &mutex, Cond &cond); | ||||
|  | ||||
| protected: | ||||
| 	/* virtual methods from AsyncInputStream */ | ||||
| @@ -120,8 +120,7 @@ protected: | ||||
|  | ||||
| private: | ||||
| 	static snd_pcm_t *OpenDevice(const char *device, int rate, | ||||
| 				     snd_pcm_format_t format, int channels, | ||||
| 				     Error &error); | ||||
| 				     snd_pcm_format_t format, int channels); | ||||
|  | ||||
| 	void Pause() { | ||||
| 		AsyncInputStream::Pause(); | ||||
| @@ -143,8 +142,7 @@ private: | ||||
| }; | ||||
|  | ||||
| inline InputStream * | ||||
| AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond, | ||||
| 			Error &error) | ||||
| AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	const char *device = StringAfterPrefix(uri, "alsa://"); | ||||
| 	if (device == nullptr) | ||||
| @@ -160,10 +158,7 @@ AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond, | ||||
| 	snd_pcm_format_t format = default_format; | ||||
| 	int channels = default_channels; | ||||
|  | ||||
| 	snd_pcm_t *handle = OpenDevice(device, rate, format, channels, | ||||
| 				       error); | ||||
| 	if (handle == nullptr) | ||||
| 		return nullptr; | ||||
| 	snd_pcm_t *handle = OpenDevice(device, rate, format, channels); | ||||
|  | ||||
| 	int frame_size = snd_pcm_format_width(format) / 8 * channels; | ||||
| 	return new AlsaInputStream(io_thread_get(), | ||||
| @@ -242,47 +237,40 @@ AlsaInputStream::Recover(int err) | ||||
| 	return err; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| static void | ||||
| ConfigureCapture(snd_pcm_t *capture_handle, | ||||
| 		 int rate, snd_pcm_format_t format, int channels, | ||||
| 		 Error &error) | ||||
| 		 int rate, snd_pcm_format_t format, int channels) | ||||
| { | ||||
| 	int err; | ||||
|  | ||||
| 	snd_pcm_hw_params_t *hw_params; | ||||
| 	if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot allocate hardware parameter structure (%s)", snd_strerror(err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot allocate hardware parameter structure (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	AtScopeExit(hw_params) { | ||||
| 		snd_pcm_hw_params_free(hw_params); | ||||
| 	}; | ||||
|  | ||||
| 	if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot initialize hardware parameter structure (%s)", snd_strerror(err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot set access type (%s)", snd_strerror (err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot set access type (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot set sample format (%s)", snd_strerror (err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot set sample format (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot set channels (%s)", snd_strerror (err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot set channels (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, rate, 0)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot set sample rate (%s)", snd_strerror (err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, rate, 0)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot set sample rate (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	/* period needs to be big enough so that poll() doesn't fire too often, | ||||
| 	 * but small enough that buffer overruns don't occur if Read() is not | ||||
| @@ -293,17 +281,13 @@ ConfigureCapture(snd_pcm_t *capture_handle, | ||||
| 	snd_pcm_uframes_t period = read_buffer_size * 2; | ||||
| 	int direction = -1; | ||||
| 	if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params, | ||||
| 							  &period, &direction)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot set period size (%s)", | ||||
| 			     snd_strerror(err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 							  &period, &direction)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot set period size (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Cannot set parameters (%s)", | ||||
| 			     snd_strerror(err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) | ||||
| 		throw FormatRuntimeError("Cannot set parameters (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	snd_pcm_sw_params_t *sw_params; | ||||
|  | ||||
| @@ -315,37 +299,31 @@ ConfigureCapture(snd_pcm_t *capture_handle, | ||||
| 	}; | ||||
|  | ||||
| 	if ((err = snd_pcm_sw_params_set_start_threshold(capture_handle, sw_params, | ||||
| 							 period)) < 0)  { | ||||
| 		error.Format(alsa_input_domain, | ||||
| 			     "unable to set start threshold (%s)", snd_strerror(err)); | ||||
| 		return false; | ||||
| 	} | ||||
| 							 period)) < 0) | ||||
| 		throw FormatRuntimeError("unable to set start threshold (%s)", | ||||
| 					 snd_strerror(err)); | ||||
|  | ||||
| 	if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0) { | ||||
| 		error.Format(alsa_input_domain, | ||||
| 			     "unable to install sw params (%s)", snd_strerror(err)); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| 	if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0) | ||||
| 		throw FormatRuntimeError("unable to install sw params (%s)", | ||||
| 					 snd_strerror(err)); | ||||
| } | ||||
|  | ||||
| inline snd_pcm_t * | ||||
| AlsaInputStream::OpenDevice(const char *device, | ||||
| 			    int rate, snd_pcm_format_t format, int channels, | ||||
| 			    Error &error) | ||||
| 			    int rate, snd_pcm_format_t format, int channels) | ||||
| { | ||||
| 	snd_pcm_t *capture_handle; | ||||
| 	int err; | ||||
| 	if ((err = snd_pcm_open(&capture_handle, device, | ||||
| 				SND_PCM_STREAM_CAPTURE, 0)) < 0) { | ||||
| 		error.Format(alsa_input_domain, "Failed to open device: %s (%s)", device, snd_strerror(err)); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 				SND_PCM_STREAM_CAPTURE, 0)) < 0) | ||||
| 		throw FormatRuntimeError("Failed to open device: %s (%s)", | ||||
| 					 device, snd_strerror(err)); | ||||
|  | ||||
| 	if (!ConfigureCapture(capture_handle, rate, format, channels, error)) { | ||||
| 	try { | ||||
| 		ConfigureCapture(capture_handle, rate, format, channels); | ||||
| 	} catch (...) { | ||||
| 		snd_pcm_close(capture_handle); | ||||
| 		return nullptr; | ||||
| 		throw; | ||||
| 	} | ||||
|  | ||||
| 	snd_pcm_prepare(capture_handle); | ||||
| @@ -356,9 +334,9 @@ AlsaInputStream::OpenDevice(const char *device, | ||||
| /*#########################  Plugin Functions  ##############################*/ | ||||
|  | ||||
| static InputStream * | ||||
| alsa_input_open(const char *uri, Mutex &mutex, Cond &cond, Error &error) | ||||
| alsa_input_open(const char *uri, Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	return AlsaInputStream::Create(uri, mutex, cond, error); | ||||
| 	return AlsaInputStream::Create(uri, mutex, cond); | ||||
| } | ||||
|  | ||||
| const struct InputPlugin input_plugin_alsa = { | ||||
|   | ||||
| @@ -25,14 +25,18 @@ | ||||
| #include "archive/ArchivePlugin.hxx" | ||||
| #include "archive/ArchiveFile.hxx" | ||||
| #include "../InputPlugin.hxx" | ||||
| #include "../InputStream.hxx" | ||||
| #include "fs/Path.hxx" | ||||
| #include "Log.hxx" | ||||
| #include "util/ScopeExit.hxx" | ||||
| #include "util/Error.hxx" | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| InputStream * | ||||
| OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond, Error &error) | ||||
| InputStreamPtr | ||||
| OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	const ArchivePlugin *arplug; | ||||
|  | ||||
| @@ -57,22 +61,21 @@ OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond, Error &error) | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	Error error; | ||||
| 	auto file = archive_file_open(arplug, Path::FromFS(archive), error); | ||||
| 	if (file == nullptr) { | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (file == nullptr) | ||||
| 		throw std::runtime_error(error.GetMessage()); | ||||
|  | ||||
| 	AtScopeExit(file) { | ||||
| 		file->Close(); | ||||
| 	}; | ||||
|  | ||||
| 	return file->OpenStream(filename, mutex, cond, error); | ||||
| 	return InputStreamPtr(file->OpenStream(filename, mutex, cond)); | ||||
| } | ||||
|  | ||||
| static InputStream * | ||||
| input_archive_open(gcc_unused const char *filename, | ||||
| 		gcc_unused Mutex &mutex, gcc_unused Cond &cond, | ||||
| 		gcc_unused Error &error) | ||||
| 		   gcc_unused Mutex &mutex, gcc_unused Cond &cond) | ||||
| { | ||||
| 	/* dummy method; use OpenArchiveInputStream() instead */ | ||||
|  | ||||
|   | ||||
| @@ -20,15 +20,15 @@ | ||||
| #ifndef MPD_INPUT_ARCHIVE_HXX | ||||
| #define MPD_INPUT_ARCHIVE_HXX | ||||
|  | ||||
| class InputStream; | ||||
| #include "input/Ptr.hxx" | ||||
|  | ||||
| class Path; | ||||
| class Mutex; | ||||
| class Cond; | ||||
| class Error; | ||||
|  | ||||
| extern const struct InputPlugin input_plugin_archive; | ||||
|  | ||||
| InputStream * | ||||
| OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond, Error &error); | ||||
| InputStreamPtr | ||||
| OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -127,7 +127,7 @@ struct cdio_uri { | ||||
| }; | ||||
|  | ||||
| static bool | ||||
| parse_cdio_uri(struct cdio_uri *dest, const char *src, Error &error) | ||||
| parse_cdio_uri(struct cdio_uri *dest, const char *src) | ||||
| { | ||||
| 	if (!StringStartsWith(src, "cdda://")) | ||||
| 		return false; | ||||
| @@ -160,10 +160,8 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, Error &error) | ||||
|  | ||||
| 	char *endptr; | ||||
| 	dest->track = strtoul(track, &endptr, 10); | ||||
| 	if (*endptr != 0) { | ||||
| 		error.Set(cdio_domain, "Malformed track number"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (*endptr != 0) | ||||
| 		throw std::runtime_error("Malformed track number"); | ||||
|  | ||||
| 	if (endptr == track) | ||||
| 		/* play the whole CD */ | ||||
| @@ -187,35 +185,28 @@ cdio_detect_device(void) | ||||
|  | ||||
| static InputStream * | ||||
| input_cdio_open(const char *uri, | ||||
| 		Mutex &mutex, Cond &cond, | ||||
| 		Error &error) | ||||
| 		Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	struct cdio_uri parsed_uri; | ||||
| 	if (!parse_cdio_uri(&parsed_uri, uri, error)) | ||||
| 	if (!parse_cdio_uri(&parsed_uri, uri)) | ||||
| 		return nullptr; | ||||
|  | ||||
| 	/* get list of CD's supporting CD-DA */ | ||||
| 	const AllocatedPath device = parsed_uri.device[0] != 0 | ||||
| 		? AllocatedPath::FromFS(parsed_uri.device) | ||||
| 		: cdio_detect_device(); | ||||
| 	if (device.IsNull()) { | ||||
| 		error.Set(cdio_domain, | ||||
| 			  "Unable find or access a CD-ROM drive with an audio CD in it."); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (device.IsNull()) | ||||
| 		throw std::runtime_error("Unable find or access a CD-ROM drive with an audio CD in it."); | ||||
|  | ||||
| 	/* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */ | ||||
| 	const auto cdio = cdio_open(device.c_str(), DRIVER_UNKNOWN); | ||||
| 	if (cdio == nullptr) { | ||||
| 		error.Set(cdio_domain, "Failed to open CD drive"); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (cdio == nullptr) | ||||
| 		throw std::runtime_error("Failed to open CD drive"); | ||||
|  | ||||
| 	const auto drv = cdio_cddap_identify_cdio(cdio, 1, nullptr); | ||||
| 	if (drv == nullptr) { | ||||
| 		error.Set(cdio_domain, "Unable to identify audio CD disc."); | ||||
| 		cdio_destroy(cdio); | ||||
| 		return nullptr; | ||||
| 		throw std::runtime_error("Unable to identify audio CD disc."); | ||||
| 	} | ||||
|  | ||||
| 	cdda_verbose_set(drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT); | ||||
| @@ -223,12 +214,12 @@ input_cdio_open(const char *uri, | ||||
| 	if (0 != cdio_cddap_open(drv)) { | ||||
| 		cdio_cddap_close_no_free_cdio(drv); | ||||
| 		cdio_destroy(cdio); | ||||
| 		error.Set(cdio_domain, "Unable to open disc."); | ||||
| 		return nullptr; | ||||
| 		throw std::runtime_error("Unable to open disc."); | ||||
| 	} | ||||
|  | ||||
| 	bool reverse_endian; | ||||
| 	switch (data_bigendianp(drv)) { | ||||
| 	const int be = data_bigendianp(drv); | ||||
| 	switch (be) { | ||||
| 	case -1: | ||||
| 		LogDebug(cdio_domain, "drive returns unknown audio data"); | ||||
| 		reverse_endian = default_reverse_endian; | ||||
| @@ -245,11 +236,10 @@ input_cdio_open(const char *uri, | ||||
| 		break; | ||||
|  | ||||
| 	default: | ||||
| 		error.Format(cdio_domain, "Drive returns unknown data type %d", | ||||
| 			     data_bigendianp(drv)); | ||||
| 		cdio_cddap_close_no_free_cdio(drv); | ||||
| 		cdio_destroy(cdio); | ||||
| 		return nullptr; | ||||
| 		throw FormatRuntimeError("Drive returns unknown data type %d", | ||||
| 					 be); | ||||
| 	} | ||||
|  | ||||
| 	lsn_t lsn_from, lsn_to; | ||||
|   | ||||
| @@ -32,6 +32,7 @@ | ||||
| #include "util/ASCII.hxx" | ||||
| #include "util/StringUtil.hxx" | ||||
| #include "util/NumberParser.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/Domain.hxx" | ||||
| #include "Log.hxx" | ||||
| @@ -78,17 +79,18 @@ struct CurlInputStream final : public AsyncInputStream { | ||||
| 				  CURL_MAX_BUFFERED, | ||||
| 				  CURL_RESUME_AT), | ||||
| 		 request_headers(nullptr), | ||||
| 		 icy(new IcyInputStream(this)) {} | ||||
| 		 icy(new IcyInputStream(this)) { | ||||
| 		InitEasy(); | ||||
| 	} | ||||
|  | ||||
| 	~CurlInputStream(); | ||||
|  | ||||
| 	CurlInputStream(const CurlInputStream &) = delete; | ||||
| 	CurlInputStream &operator=(const CurlInputStream &) = delete; | ||||
|  | ||||
| 	static InputStream *Open(const char *url, Mutex &mutex, Cond &cond, | ||||
| 				 Error &error); | ||||
| 	static InputStream *Open(const char *url, Mutex &mutex, Cond &cond); | ||||
|  | ||||
| 	bool InitEasy(Error &error); | ||||
| 	void InitEasy(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Frees the current "libcurl easy" handle, and everything | ||||
| @@ -201,7 +203,7 @@ public: | ||||
| 		curl_multi_cleanup(multi); | ||||
| 	} | ||||
|  | ||||
| 	bool Add(CurlInputStream *c, Error &error); | ||||
| 	void Add(CurlInputStream *c); | ||||
| 	void Remove(CurlInputStream *c); | ||||
|  | ||||
| 	/** | ||||
| @@ -355,41 +357,39 @@ CurlSocket::OnSocketReady(unsigned flags) | ||||
|  | ||||
| /** | ||||
|  * Runs in the I/O thread.  No lock needed. | ||||
|  * | ||||
|  * Throws std::runtime_error on error. | ||||
|  */ | ||||
| inline bool | ||||
| CurlMulti::Add(CurlInputStream *c, Error &error) | ||||
| inline void | ||||
| CurlMulti::Add(CurlInputStream *c) | ||||
| { | ||||
| 	assert(io_thread_inside()); | ||||
| 	assert(c != nullptr); | ||||
| 	assert(c->easy != nullptr); | ||||
|  | ||||
| 	CURLMcode mcode = curl_multi_add_handle(multi, c->easy); | ||||
| 	if (mcode != CURLM_OK) { | ||||
| 		error.Format(curlm_domain, mcode, | ||||
| 			     "curl_multi_add_handle() failed: %s", | ||||
| 			     curl_multi_strerror(mcode)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (mcode != CURLM_OK) | ||||
| 		throw FormatRuntimeError("curl_multi_add_handle() failed: %s", | ||||
| 					 curl_multi_strerror(mcode)); | ||||
|  | ||||
| 	InvalidateSockets(); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Call input_curl_easy_add() in the I/O thread.  May be called from | ||||
|  * any thread.  Caller must not hold a mutex. | ||||
|  * | ||||
|  * Throws std::runtime_error on error. | ||||
|  */ | ||||
| static bool | ||||
| input_curl_easy_add_indirect(CurlInputStream *c, Error &error) | ||||
| static void | ||||
| input_curl_easy_add_indirect(CurlInputStream *c) | ||||
| { | ||||
| 	assert(c != nullptr); | ||||
| 	assert(c->easy != nullptr); | ||||
|  | ||||
| 	bool result; | ||||
| 	BlockingCall(io_thread_get(), [c, &error, &result](){ | ||||
| 			result = curl_multi->Add(c, error); | ||||
| 	BlockingCall(io_thread_get(), [c](){ | ||||
| 			curl_multi->Add(c); | ||||
| 		}); | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| inline void | ||||
| @@ -727,14 +727,12 @@ input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream) | ||||
| 	return c.DataReceived(ptr, size); | ||||
| } | ||||
|  | ||||
| bool | ||||
| CurlInputStream::InitEasy(Error &error) | ||||
| void | ||||
| CurlInputStream::InitEasy() | ||||
| { | ||||
| 	easy = curl_easy_init(); | ||||
| 	if (easy == nullptr) { | ||||
| 		error.Set(curl_domain, "curl_easy_init() failed"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (easy == nullptr) | ||||
| 		throw std::runtime_error("curl_easy_init() failed"); | ||||
|  | ||||
| 	curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this); | ||||
| 	curl_easy_setopt(easy, CURLOPT_USERAGENT, | ||||
| @@ -773,19 +771,14 @@ CurlInputStream::InitEasy(Error &error) | ||||
| 	curl_easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l); | ||||
|  | ||||
| 	CURLcode code = curl_easy_setopt(easy, CURLOPT_URL, GetURI()); | ||||
| 	if (code != CURLE_OK) { | ||||
| 		error.Format(curl_domain, code, | ||||
| 			     "curl_easy_setopt() failed: %s", | ||||
| 			     curl_easy_strerror(code)); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (code != CURLE_OK) | ||||
| 		throw FormatRuntimeError("curl_easy_setopt() failed: %s", | ||||
| 					 curl_easy_strerror(code)); | ||||
|  | ||||
| 	request_headers = nullptr; | ||||
| 	request_headers = curl_slist_append(request_headers, | ||||
| 					       "Icy-Metadata: 1"); | ||||
| 	curl_easy_setopt(easy, CURLOPT_HTTPHEADER, request_headers); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| @@ -809,11 +802,11 @@ CurlInputStream::DoSeek(offset_type new_offset) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	Error error; | ||||
| 	if (!InitEasy(postponed_error)) { | ||||
| 	try { | ||||
| 		InitEasy(); | ||||
| 	} catch (...) { | ||||
| 		mutex.lock(); | ||||
| 		PostponeError(std::move(error)); | ||||
| 		return; | ||||
| 		throw; | ||||
| 	} | ||||
|  | ||||
| 	/* send the "Range" header */ | ||||
| @@ -823,10 +816,11 @@ CurlInputStream::DoSeek(offset_type new_offset) | ||||
| 		curl_easy_setopt(easy, CURLOPT_RANGE, range); | ||||
| 	} | ||||
|  | ||||
| 	if (!input_curl_easy_add_indirect(this, error)) { | ||||
| 	try { | ||||
| 		input_curl_easy_add_indirect(this); | ||||
| 	} catch (...) { | ||||
| 		mutex.lock(); | ||||
| 		PostponeError(std::move(error)); | ||||
| 		return; | ||||
| 		throw; | ||||
| 	} | ||||
|  | ||||
| 	mutex.lock(); | ||||
| @@ -834,27 +828,29 @@ CurlInputStream::DoSeek(offset_type new_offset) | ||||
| } | ||||
|  | ||||
| inline InputStream * | ||||
| CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond, | ||||
| 		      Error &error) | ||||
| CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	CurlInputStream *c = new CurlInputStream(url, mutex, cond); | ||||
| 	if (!c->InitEasy(error) || !input_curl_easy_add_indirect(c, error)) { | ||||
|  | ||||
| 	try { | ||||
| 		c->InitEasy(); | ||||
| 		input_curl_easy_add_indirect(c); | ||||
| 	} catch (...) { | ||||
| 		delete c; | ||||
| 		return nullptr; | ||||
| 		throw; | ||||
| 	} | ||||
|  | ||||
| 	return c->icy; | ||||
| } | ||||
|  | ||||
| static InputStream * | ||||
| input_curl_open(const char *url, Mutex &mutex, Cond &cond, | ||||
| 		Error &error) | ||||
| input_curl_open(const char *url, Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	if (memcmp(url, "http://",  7) != 0 && | ||||
| 	    memcmp(url, "https://", 8) != 0) | ||||
| 		return nullptr; | ||||
|  | ||||
| 	return CurlInputStream::Open(url, mutex, cond, error); | ||||
| 	return CurlInputStream::Open(url, mutex, cond); | ||||
| } | ||||
|  | ||||
| const struct InputPlugin input_plugin_curl = { | ||||
|   | ||||
| @@ -84,8 +84,7 @@ input_ffmpeg_init(gcc_unused const ConfigBlock &block) | ||||
|  | ||||
| static InputStream * | ||||
| input_ffmpeg_open(const char *uri, | ||||
| 		  Mutex &mutex, Cond &cond, | ||||
| 		  Error &error) | ||||
| 		  Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	if (!StringStartsWith(uri, "gopher://") && | ||||
| 	    !StringStartsWith(uri, "rtp://") && | ||||
| @@ -97,10 +96,8 @@ input_ffmpeg_open(const char *uri, | ||||
|  | ||||
| 	AVIOContext *h; | ||||
| 	auto result = avio_open(&h, uri, AVIO_FLAG_READ); | ||||
| 	if (result != 0) { | ||||
| 		SetFfmpegError(error, result); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (result != 0) | ||||
| 		throw MakeFfmpegError(result); | ||||
|  | ||||
| 	return new FfmpegInputStream(uri, mutex, cond, h); | ||||
| } | ||||
|   | ||||
| @@ -21,18 +21,15 @@ | ||||
| #include "FileInputPlugin.hxx" | ||||
| #include "../InputStream.hxx" | ||||
| #include "../InputPlugin.hxx" | ||||
| #include "util/Error.hxx" | ||||
| #include "util/Domain.hxx" | ||||
| #include "fs/Path.hxx" | ||||
| #include "fs/FileInfo.hxx" | ||||
| #include "fs/io/FileReader.hxx" | ||||
| #include "system/FileDescriptor.hxx" | ||||
| #include "util/RuntimeError.hxx" | ||||
|  | ||||
| #include <sys/stat.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| static constexpr Domain file_domain("file"); | ||||
|  | ||||
| class FileInputStream final : public InputStream { | ||||
| 	FileReader reader; | ||||
|  | ||||
| @@ -56,38 +53,31 @@ public: | ||||
| 	bool Seek(offset_type offset, Error &error) override; | ||||
| }; | ||||
|  | ||||
| InputStream * | ||||
| InputStreamPtr | ||||
| OpenFileInputStream(Path path, | ||||
| 		    Mutex &mutex, Cond &cond, | ||||
| 		    Error &error) | ||||
| try { | ||||
| 		    Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	FileReader reader(path); | ||||
|  | ||||
| 	const FileInfo info = reader.GetFileInfo(); | ||||
|  | ||||
| 	if (!info.IsRegular()) { | ||||
| 		error.Format(file_domain, "Not a regular file: %s", | ||||
| 			     path.c_str()); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (!info.IsRegular()) | ||||
| 		throw FormatRuntimeError("Not a regular file: %s", | ||||
| 					 path.c_str()); | ||||
|  | ||||
| #ifdef POSIX_FADV_SEQUENTIAL | ||||
| 	posix_fadvise(reader.GetFD().Get(), (off_t)0, info.GetSize(), | ||||
| 		      POSIX_FADV_SEQUENTIAL); | ||||
| #endif | ||||
|  | ||||
| 	return new FileInputStream(path.ToUTF8().c_str(), | ||||
| 				   std::move(reader), info.GetSize(), | ||||
| 				   mutex, cond); | ||||
| } catch (const std::exception &e) { | ||||
| 	error.Set(std::current_exception()); | ||||
| 	return nullptr; | ||||
| 	return InputStreamPtr(new FileInputStream(path.ToUTF8().c_str(), | ||||
| 						  std::move(reader), info.GetSize(), | ||||
| 						  mutex, cond)); | ||||
| } | ||||
|  | ||||
| static InputStream * | ||||
| input_file_open(gcc_unused const char *filename, | ||||
| 		gcc_unused Mutex &mutex, gcc_unused Cond &cond, | ||||
| 		gcc_unused Error &error) | ||||
| 		gcc_unused Mutex &mutex, gcc_unused Cond &cond) | ||||
| { | ||||
| 	/* dummy method; use OpenFileInputStream() instead */ | ||||
|  | ||||
|   | ||||
| @@ -20,17 +20,16 @@ | ||||
| #ifndef MPD_INPUT_FILE_HXX | ||||
| #define MPD_INPUT_FILE_HXX | ||||
|  | ||||
| class InputStream; | ||||
| #include "input/Ptr.hxx" | ||||
|  | ||||
| class Path; | ||||
| class Mutex; | ||||
| class Cond; | ||||
| class Error; | ||||
|  | ||||
| extern const struct InputPlugin input_plugin_file; | ||||
|  | ||||
| InputStream * | ||||
| InputStreamPtr | ||||
| OpenFileInputStream(Path path, | ||||
| 		    Mutex &mutex, Cond &cond, | ||||
| 		    Error &error); | ||||
| 		    Mutex &mutex, Cond &cond); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -72,8 +72,7 @@ MmsInputStream::Open(Error &error) | ||||
|  | ||||
| static InputStream * | ||||
| input_mms_open(const char *url, | ||||
| 	       Mutex &mutex, Cond &cond, | ||||
| 	       gcc_unused Error &error) | ||||
| 	       Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	if (!StringStartsWith(url, "mms://") && | ||||
| 	    !StringStartsWith(url, "mmsh://") && | ||||
|   | ||||
| @@ -57,10 +57,10 @@ public: | ||||
| 		DeferClose(); | ||||
| 	} | ||||
|  | ||||
| 	bool Open(Error &error) { | ||||
| 	void Open() { | ||||
| 		assert(!IsReady()); | ||||
|  | ||||
| 		return NfsFileReader::Open(GetURI(), error); | ||||
| 		NfsFileReader::Open(GetURI()); | ||||
| 	} | ||||
|  | ||||
| private: | ||||
| @@ -119,17 +119,10 @@ NfsInputStream::DoResume() | ||||
| 		reconnect_on_resume = false; | ||||
| 		reconnecting = true; | ||||
|  | ||||
| 		mutex.unlock(); | ||||
| 		ScopeUnlock unlock(mutex); | ||||
|  | ||||
| 		NfsFileReader::Close(); | ||||
|  | ||||
| 		Error error; | ||||
| 		bool success = NfsFileReader::Open(GetURI(), error); | ||||
| 		mutex.lock(); | ||||
|  | ||||
| 		if (!success) { | ||||
| 			postponed_error = std::move(error); | ||||
| 			cond.broadcast(); | ||||
| 		} | ||||
| 		NfsFileReader::Open(GetURI()); | ||||
|  | ||||
| 		return; | ||||
| 	} | ||||
| @@ -229,16 +222,17 @@ input_nfs_finish() | ||||
|  | ||||
| static InputStream * | ||||
| input_nfs_open(const char *uri, | ||||
| 	       Mutex &mutex, Cond &cond, | ||||
| 	       Error &error) | ||||
| 	       Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	if (!StringStartsWith(uri, "nfs://")) | ||||
| 		return nullptr; | ||||
|  | ||||
| 	NfsInputStream *is = new NfsInputStream(uri, mutex, cond); | ||||
| 	if (!is->Open(error)) { | ||||
| 	try { | ||||
| 		is->Open(); | ||||
| 	} catch (...) { | ||||
| 		delete is; | ||||
| 		return nullptr; | ||||
| 		throw; | ||||
| 	} | ||||
|  | ||||
| 	return is; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "../InputStream.hxx" | ||||
| #include "../InputPlugin.hxx" | ||||
| #include "PluginUnavailable.hxx" | ||||
| #include "system/Error.hxx" | ||||
| #include "util/StringCompare.hxx" | ||||
| #include "util/Error.hxx" | ||||
|  | ||||
| @@ -85,8 +86,7 @@ input_smbclient_init(gcc_unused const ConfigBlock &block) | ||||
|  | ||||
| static InputStream * | ||||
| input_smbclient_open(const char *uri, | ||||
| 		     Mutex &mutex, Cond &cond, | ||||
| 		     Error &error) | ||||
| 		     Mutex &mutex, Cond &cond) | ||||
| { | ||||
| 	if (!StringStartsWith(uri, "smb://")) | ||||
| 		return nullptr; | ||||
| @@ -94,33 +94,30 @@ input_smbclient_open(const char *uri, | ||||
| 	const ScopeLock protect(smbclient_mutex); | ||||
|  | ||||
| 	SMBCCTX *ctx = smbc_new_context(); | ||||
| 	if (ctx == nullptr) { | ||||
| 		error.SetErrno("smbc_new_context() failed"); | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	if (ctx == nullptr) | ||||
| 		throw MakeErrno("smbc_new_context() failed"); | ||||
|  | ||||
| 	SMBCCTX *ctx2 = smbc_init_context(ctx); | ||||
| 	if (ctx2 == nullptr) { | ||||
| 		error.SetErrno("smbc_init_context() failed"); | ||||
| 		int e = errno; | ||||
| 		smbc_free_context(ctx, 1); | ||||
| 		return nullptr; | ||||
| 		throw MakeErrno(e, "smbc_init_context() failed"); | ||||
| 	} | ||||
|  | ||||
| 	ctx = ctx2; | ||||
|  | ||||
| 	int fd = smbc_open(uri, O_RDONLY, 0); | ||||
| 	if (fd < 0) { | ||||
| 		error.SetErrno("smbc_open() failed"); | ||||
| 		int e = errno; | ||||
| 		smbc_free_context(ctx, 1); | ||||
| 		return nullptr; | ||||
| 		throw MakeErrno(e, "smbc_open() failed"); | ||||
| 	} | ||||
|  | ||||
| 	struct stat st; | ||||
| 	if (smbc_fstat(fd, &st) < 0) { | ||||
| 		error.SetErrno("smbc_fstat() failed"); | ||||
| 		smbc_close(fd); | ||||
| 		int e = errno; | ||||
| 		smbc_free_context(ctx, 1); | ||||
| 		return nullptr; | ||||
| 		throw MakeErrno(e, "smbc_fstat() failed"); | ||||
| 	} | ||||
|  | ||||
| 	return new SmbclientInputStream(uri, mutex, cond, ctx, fd, st); | ||||
|   | ||||
| @@ -26,6 +26,14 @@ extern "C" { | ||||
| #include <libavutil/error.h> | ||||
| } | ||||
|  | ||||
| std::runtime_error | ||||
| MakeFfmpegError(int errnum) | ||||
| { | ||||
| 	char msg[256]; | ||||
| 	av_strerror(errnum, msg, sizeof(msg)); | ||||
| 	return std::runtime_error(msg); | ||||
| } | ||||
|  | ||||
| void | ||||
| SetFfmpegError(Error &error, int errnum) | ||||
| { | ||||
|   | ||||
| @@ -20,8 +20,13 @@ | ||||
| #ifndef MPD_FFMPEG_ERROR_HXX | ||||
| #define MPD_FFMPEG_ERROR_HXX | ||||
|  | ||||
| #include <stdexcept> | ||||
|  | ||||
| class Error; | ||||
|  | ||||
| std::runtime_error | ||||
| MakeFfmpegError(int errnum); | ||||
|  | ||||
| void | ||||
| SetFfmpegError(Error &error, int errnum); | ||||
|  | ||||
|   | ||||
| @@ -91,23 +91,19 @@ NfsFileReader::DeferClose() | ||||
| 	BlockingCall(io_thread_get(), [this](){ Close(); }); | ||||
| } | ||||
|  | ||||
| bool | ||||
| NfsFileReader::Open(const char *uri, Error &error) | ||||
| void | ||||
| NfsFileReader::Open(const char *uri) | ||||
| { | ||||
| 	assert(state == State::INITIAL); | ||||
|  | ||||
| 	if (!StringStartsWith(uri, "nfs://")) { | ||||
| 		error.Set(nfs_domain, "Malformed nfs:// URI"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (!StringStartsWith(uri, "nfs://")) | ||||
| 		throw std::runtime_error("Malformed nfs:// URI"); | ||||
|  | ||||
| 	uri += 6; | ||||
|  | ||||
| 	const char *slash = strchr(uri, '/'); | ||||
| 	if (slash == nullptr) { | ||||
| 		error.Set(nfs_domain, "Malformed nfs:// URI"); | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (slash == nullptr) | ||||
| 		throw std::runtime_error("Malformed nfs:// URI"); | ||||
|  | ||||
| 	server = std::string(uri, slash); | ||||
|  | ||||
| @@ -121,10 +117,8 @@ NfsFileReader::Open(const char *uri, Error &error) | ||||
| 		path = new_path; | ||||
| 	} else { | ||||
| 		slash = strrchr(uri + 1, '/'); | ||||
| 		if (slash == nullptr || slash[1] == 0) { | ||||
| 			error.Set(nfs_domain, "Malformed nfs:// URI"); | ||||
| 			return false; | ||||
| 		} | ||||
| 		if (slash == nullptr || slash[1] == 0) | ||||
| 			throw std::runtime_error("Malformed nfs:// URI"); | ||||
|  | ||||
| 		export_name = std::string(uri, slash); | ||||
| 		path = slash; | ||||
| @@ -132,7 +126,6 @@ NfsFileReader::Open(const char *uri, Error &error) | ||||
|  | ||||
| 	state = State::DEFER; | ||||
| 	DeferredMonitor::Schedule(); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool | ||||
|   | ||||
| @@ -61,7 +61,11 @@ public: | ||||
| 	void Close(); | ||||
| 	void DeferClose(); | ||||
|  | ||||
| 	bool Open(const char *uri, Error &error); | ||||
| 	/** | ||||
| 	 * Throws std::runtime_error on error. | ||||
| 	 */ | ||||
| 	void Open(const char *uri); | ||||
|  | ||||
| 	bool Read(uint64_t offset, size_t size, Error &error); | ||||
| 	void CancelRead(); | ||||
|  | ||||
|   | ||||
| @@ -44,13 +44,7 @@ try { | ||||
| 	if (!playlist_suffix_supported(suffix_utf8.c_str())) | ||||
| 		return nullptr; | ||||
|  | ||||
| 	Error error; | ||||
| 	auto is = OpenLocalInputStream(path, mutex, cond, error); | ||||
| 	if (is == nullptr) { | ||||
| 		LogError(error); | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	auto is = OpenLocalInputStream(path, mutex, cond); | ||||
| 	return playlist_list_open_stream_suffix(std::move(is), | ||||
| 						suffix_utf8.c_str()); | ||||
| } catch (const std::runtime_error &e) { | ||||
| @@ -85,15 +79,7 @@ try { | ||||
| 	if (playlist != nullptr) | ||||
| 		return playlist; | ||||
|  | ||||
| 	Error error; | ||||
| 	auto is = InputStream::OpenReady(uri, mutex, cond, error); | ||||
| 	if (is == nullptr) { | ||||
| 		if (error.IsDefined()) | ||||
| 			FormatError(error, "Failed to open %s", uri); | ||||
|  | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	auto is = InputStream::OpenReady(uri, mutex, cond); | ||||
| 	return playlist_list_open_stream(std::move(is), uri); | ||||
| } catch (const std::runtime_error &e) { | ||||
| 	LogError(e); | ||||
|   | ||||
| @@ -230,15 +230,9 @@ static constexpr yajl_callbacks parse_callbacks = { | ||||
| static int | ||||
| soundcloud_parse_json(const char *url, yajl_handle hand, | ||||
| 		      Mutex &mutex, Cond &cond) | ||||
| { | ||||
| try { | ||||
| 	Error error; | ||||
| 	auto input_stream = InputStream::OpenReady(url, mutex, cond, | ||||
| 						   error); | ||||
| 	if (input_stream == nullptr) { | ||||
| 		if (error.IsDefined()) | ||||
| 			LogError(error); | ||||
| 		return -1; | ||||
| 	} | ||||
| 	auto input_stream = InputStream::OpenReady(url, mutex, cond); | ||||
|  | ||||
| 	const ScopeLock protect(mutex); | ||||
|  | ||||
| @@ -275,6 +269,9 @@ soundcloud_parse_json(const char *url, yajl_handle hand, | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } catch (const std::exception &e) { | ||||
| 	LogError(e); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
| @@ -140,6 +140,19 @@ IsFileNotFound(const std::system_error &e) | ||||
| #endif | ||||
| } | ||||
|  | ||||
| gcc_pure | ||||
| static inline bool | ||||
| IsPathNotFound(const std::system_error &e) | ||||
| { | ||||
| #ifdef WIN32 | ||||
| 	return e.code().category() == std::system_category() && | ||||
| 		e.code().value() == ERROR_PATH_NOT_FOUND; | ||||
| #else | ||||
| 	return e.code().category() == std::system_category() && | ||||
| 		e.code().value() == ENOTDIR; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| gcc_pure | ||||
| static inline bool | ||||
| IsAccessDenied(const std::system_error &e) | ||||
|   | ||||
| @@ -53,13 +53,7 @@ try { | ||||
| 	Mutex mutex; | ||||
| 	Cond cond; | ||||
|  | ||||
| 	Error error; | ||||
| 	auto is = OpenLocalInputStream(path, mutex, cond, error); | ||||
| 	if (!is) { | ||||
| 		LogError(error); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	auto is = OpenLocalInputStream(path, mutex, cond); | ||||
| 	return ScanGenericTags(*is, handler, ctx); | ||||
| } catch (const std::runtime_error &e) { | ||||
| 	LogError(e); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann