diff options
Diffstat (limited to '')
1017 files changed, 15869 insertions, 8754 deletions
diff --git a/src/AudioConfig.cxx b/src/AudioConfig.cxx index d54f59e17..8ee17f301 100644 --- a/src/AudioConfig.cxx +++ b/src/AudioConfig.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "AudioConfig.hxx" #include "AudioFormat.hxx" #include "AudioParser.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "util/Error.hxx" @@ -39,7 +39,7 @@ getOutputAudioFormat(AudioFormat inAudioFormat) void initAudioConfig(void) { - const struct config_param *param = config_get_param(CONF_AUDIO_OUTPUT_FORMAT); + const struct config_param *param = config_get_param(ConfigOption::AUDIO_OUTPUT_FORMAT); if (param == nullptr) return; diff --git a/src/AudioConfig.hxx b/src/AudioConfig.hxx index 471e60e51..1056eb8ff 100644 --- a/src/AudioConfig.hxx +++ b/src/AudioConfig.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,6 +26,7 @@ AudioFormat getOutputAudioFormat(AudioFormat inFormat); /* make sure initPlayerData is called before this function!! */ -void initAudioConfig(void); +void +initAudioConfig(); #endif diff --git a/src/AudioFormat.cxx b/src/AudioFormat.cxx index edfb9d8fe..fb30dd93d 100644 --- a/src/AudioFormat.cxx +++ b/src/AudioFormat.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/AudioFormat.hxx b/src/AudioFormat.hxx index 0937ab8ae..a00e761a8 100644 --- a/src/AudioFormat.hxx +++ b/src/AudioFormat.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -99,8 +99,8 @@ struct AudioFormat { } /** - * Clears the #audio_format object, i.e. sets all attributes to an - * undefined (invalid) value. + * Clears the object, i.e. sets all attributes to an undefined + * (invalid) value. */ void Clear() { sample_rate = 0; @@ -185,8 +185,6 @@ audio_valid_sample_rate(unsigned sample_rate) /** * Checks whether the sample format is valid. - * - * @param bits the number of significant bits per sample */ static inline bool audio_valid_sample_format(SampleFormat format) @@ -289,10 +287,10 @@ AudioFormat::GetTimeToSize() const } /** - * Renders a #sample_format enum into a string, e.g. for printing it + * Renders a #SampleFormat enum into a string, e.g. for printing it * in a log file. * - * @param format a #sample_format enum value + * @param format a #SampleFormat enum value * @return the string */ gcc_pure gcc_malloc @@ -300,12 +298,12 @@ const char * sample_format_to_string(SampleFormat format); /** - * Renders the #audio_format object into a string, e.g. for printing + * Renders the #AudioFormat object into a string, e.g. for printing * it in a log file. * - * @param af the #audio_format object + * @param af the #AudioFormat object * @param s a buffer to print into - * @return the string, or nullptr if the #audio_format object is invalid + * @return the string, or nullptr if the #AudioFormat object is invalid */ gcc_pure gcc_malloc const char * diff --git a/src/AudioParser.cxx b/src/AudioParser.cxx index 74bb04abc..b7051b4b2 100644 --- a/src/AudioParser.cxx +++ b/src/AudioParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/AudioParser.hxx b/src/AudioParser.hxx index 07ad7cb4a..247d0d6f0 100644 --- a/src/AudioParser.hxx +++ b/src/AudioParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -30,12 +30,12 @@ class Error; /** * Parses a string in the form "SAMPLE_RATE:BITS:CHANNELS" into an - * #audio_format. + * #AudioFormat. * * @param dest the destination #audio_format struct * @param src the input string * @param mask if true, then "*" is allowed for any number of items - * @param error_r location to store the error occurring, or NULL to + * @param error location to store the error occurring, or NULL to * ignore errors * @return true on success */ diff --git a/src/BulkEdit.hxx b/src/BulkEdit.hxx index 422dc4f38..89c148f0c 100644 --- a/src/BulkEdit.hxx +++ b/src/BulkEdit.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/CheckAudioFormat.cxx b/src/CheckAudioFormat.cxx index 03e67e07e..3f4176dc1 100644 --- a/src/CheckAudioFormat.cxx +++ b/src/CheckAudioFormat.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/CheckAudioFormat.hxx b/src/CheckAudioFormat.hxx index 67bd88a61..25d82cd5b 100644 --- a/src/CheckAudioFormat.hxx +++ b/src/CheckAudioFormat.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Chrono.hxx b/src/Chrono.hxx index cc87c5ba1..d71202841 100644 --- a/src/Chrono.hxx +++ b/src/Chrono.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #include <utility> #include <cstdint> -#if defined(__GNUC__) && !GCC_CHECK_VERSION(4,7) && !defined(__clang__) +#if GCC_OLDER_THAN(4,7) /* std::chrono::duration operators are "constexpr" since gcc 4.7 */ #define chrono_constexpr gcc_pure #else diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx index c6e9c69c5..b59a39e40 100644 --- a/src/CommandLine.cxx +++ b/src/CommandLine.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include "fs/Traits.hxx" #include "fs/FileSystem.hxx" #include "fs/StandardDirectory.hxx" +#include "util/Macros.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/OptionDef.hxx" @@ -66,12 +67,12 @@ #include <stdlib.h> #ifdef WIN32 -#define CONFIG_FILE_LOCATION "mpd\\mpd.conf" -#define APP_CONFIG_FILE_LOCATION "conf\\mpd.conf" +#define CONFIG_FILE_LOCATION PATH_LITERAL("mpd\\mpd.conf") +#define APP_CONFIG_FILE_LOCATION PATH_LITERAL("conf\\mpd.conf") #else -#define USER_CONFIG_FILE_LOCATION1 ".mpdconf" -#define USER_CONFIG_FILE_LOCATION2 ".mpd/mpd.conf" -#define USER_CONFIG_FILE_LOCATION_XDG "mpd/mpd.conf" +#define USER_CONFIG_FILE_LOCATION1 PATH_LITERAL(".mpdconf") +#define USER_CONFIG_FILE_LOCATION2 PATH_LITERAL(".mpd/mpd.conf") +#define USER_CONFIG_FILE_LOCATION_XDG PATH_LITERAL("mpd/mpd.conf") #endif static constexpr OptionDef opt_kill( @@ -98,40 +99,44 @@ static constexpr Domain cmdline_domain("cmdline"); gcc_noreturn static void version(void) { - puts("Music Player Daemon " VERSION + printf("Music Player Daemon " VERSION #ifdef GIT_COMMIT - " (" GIT_COMMIT ")" + " (" GIT_COMMIT ")" #endif - "\n" - "\n" - "Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n" - "Copyright (C) 2008-2014 Max Kellermann <max@duempel.org>\n" - "This is free software; see the source for copying conditions. There is NO\n" - "warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + "\n" + "\n" + "Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n" + "Copyright (C) 2008-2015 Max Kellermann <max@duempel.org>\n" + "This is free software; see the source for copying conditions. There is NO\n" + "warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" #ifdef ENABLE_DATABASE - puts("\n" - "Database plugins:"); + "\n" + "Database plugins:\n"); for (auto i = database_plugins; *i != nullptr; ++i) printf(" %s", (*i)->name); - puts("\n\n" - "Storage plugins:"); + printf("\n\n" + "Storage plugins:\n"); for (auto i = storage_plugins; *i != nullptr; ++i) printf(" %s", (*i)->name); + + printf("\n" #endif #ifdef ENABLE_NEIGHBOR_PLUGINS - puts("\n\n" - "Neighbor plugins:"); + "\n" + "Neighbor plugins:\n"); for (auto i = neighbor_plugins; *i != nullptr; ++i) printf(" %s", (*i)->name); + + printf("\n" #endif - puts("\n\n" - "Decoders plugins:"); + "\n" + "Decoders plugins:\n"); decoder_plugins_for_each([](const DecoderPlugin &plugin){ printf(" [%s]", plugin.name); @@ -141,26 +146,39 @@ static void version(void) for (; *suffixes != nullptr; ++suffixes) printf(" %s", *suffixes); - puts(""); + printf("\n"); }); - puts("\n" - "Output plugins:"); + printf("\n" + "Filters:\n" +#ifdef ENABLE_LIBSAMPLERATE + " libsamplerate" +#endif +#ifdef ENABLE_SOXR + " soxr" +#endif + "\n\n" + "Tag plugins:\n" +#ifdef ENABLE_ID3TAG + " id3tag" +#endif + "\n\n" + "Output plugins:\n"); audio_output_plugins_for_each(plugin) printf(" %s", plugin->name); - puts(""); + printf("\n" #ifdef ENABLE_ENCODER - puts("\n" - "Encoder plugins:"); + "\n" + "Encoder plugins:\n"); encoder_plugins_for_each(plugin) printf(" %s", plugin->name); - puts(""); + printf("\n" #endif #ifdef ENABLE_ARCHIVE - puts("\n" - "Archive plugins:"); + "\n" + "Archive plugins:\n"); archive_plugins_for_each(plugin) { printf(" [%s]", plugin->name); @@ -169,24 +187,57 @@ static void version(void) for (; *suffixes != nullptr; ++suffixes) printf(" %s", *suffixes); - puts(""); + printf("\n"); } + + printf("" #endif - puts("\n" - "Input plugins:"); + "\n" + "Input plugins:\n"); input_plugins_for_each(plugin) printf(" %s", plugin->name); - puts("\n\n" - "Playlist plugins:"); + printf("\n\n" + "Playlist plugins:\n"); playlist_plugins_for_each(plugin) printf(" %s", plugin->name); - puts("\n\n" - "Protocols:"); + printf("\n\n" + "Protocols:\n"); print_supported_uri_schemes_to_fp(stdout); + printf("\n" + "Other features:\n" +#ifdef HAVE_AVAHI + " avahi" +#endif +#ifdef USE_EPOLL + " epoll" +#endif +#ifdef HAVE_ICONV + " iconv" +#endif +#ifdef HAVE_ICU + " icu" +#endif +#ifdef ENABLE_INOTIFY + " inotify" +#endif +#ifdef HAVE_IPV6 + " ipv6" +#endif +#ifdef ENABLE_SYSTEMD_DAEMON + " systemd" +#endif +#ifdef HAVE_TCP + " tcp" +#endif +#ifdef HAVE_UN + " un" +#endif + "\n"); + exit(EXIT_SUCCESS); } @@ -206,12 +257,12 @@ static void PrintOption(const OptionDef &opt) gcc_noreturn static void help(void) { - puts("Usage:\n" - " mpd [OPTION...] [path/to/mpd.conf]\n" - "\n" - "Music Player Daemon - a daemon for playing music.\n" - "\n" - "Options:"); + printf("Usage:\n" + " mpd [OPTION...] [path/to/mpd.conf]\n" + "\n" + "Music Player Daemon - a daemon for playing music.\n" + "\n" + "Options:\n"); PrintOption(opt_help); PrintOption(opt_kill); @@ -326,7 +377,19 @@ parse_cmdline(int argc, char **argv, struct options *options, if (config_file != nullptr) { /* use specified configuration file */ +#ifdef _UNICODE + wchar_t buffer[MAX_PATH]; + auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1, + buffer, ARRAY_SIZE(buffer)); + if (result <= 0) { + error.SetLastError("MultiByteToWideChar() failed"); + return false; + } + + return ReadConfigFile(Path::FromFS(buffer), error); +#else return ReadConfigFile(Path::FromFS(config_file), error); +#endif } /* use default configuration file path */ diff --git a/src/CommandLine.hxx b/src/CommandLine.hxx index d8dedb1fb..38adfdc4d 100644 --- a/src/CommandLine.hxx +++ b/src/CommandLine.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Compiler.h b/src/Compiler.h index fea971526..87142fa08 100644 --- a/src/Compiler.h +++ b/src/Compiler.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,8 +28,20 @@ #define GCC_VERSION 0 #endif +/** + * Are we building with the specified version of gcc (not clang or any + * other compiler) or newer? + */ #define GCC_CHECK_VERSION(major, minor) \ - (defined(__GNUC__) && GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0)) + (defined(__GNUC__) && !defined(__clang__) && \ + GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0)) + +/** + * Are we building with clang (any version) or at least the specified + * gcc version? + */ +#define CLANG_OR_GCC_VERSION(major, minor) \ + (defined(__clang__) || GCC_CHECK_VERSION(major, minor)) /** * Are we building with gcc (not clang or any other compiler) and a @@ -59,7 +71,7 @@ (defined(__clang__) && \ CLANG_VERSION >= GCC_MAKE_VERSION(major, minor, 0)) -#if GCC_CHECK_VERSION(4,0) +#if CLANG_OR_GCC_VERSION(4,0) /* GCC 4.x */ @@ -119,7 +131,7 @@ #endif -#if GCC_CHECK_VERSION(4,3) +#if CLANG_OR_GCC_VERSION(4,3) #define gcc_hot __attribute__((hot)) #define gcc_cold __attribute__((cold)) @@ -131,7 +143,7 @@ #endif /* ! GCC_UNUSED >= 40300 */ -#if GCC_CHECK_VERSION(4,6) && !defined(__clang__) +#if GCC_CHECK_VERSION(4,6) #define gcc_flatten __attribute__((flatten)) #else #define gcc_flatten @@ -140,7 +152,7 @@ #ifndef __cplusplus /* plain C99 has "restrict" */ #define gcc_restrict restrict -#elif GCC_CHECK_VERSION(4,0) +#elif CLANG_OR_GCC_VERSION(4,0) /* "__restrict__" is a GCC extension for C++ */ #define gcc_restrict __restrict__ #else @@ -158,7 +170,7 @@ #define final #endif -#if defined(__clang__) || GCC_CHECK_VERSION(4,8) +#if CLANG_OR_GCC_VERSION(4,8) #define gcc_alignas(T, fallback) alignas(T) #else #define gcc_alignas(T, fallback) gcc_aligned(fallback) diff --git a/src/DetachedSong.cxx b/src/DetachedSong.cxx index 906e29bba..ca914498b 100644 --- a/src/DetachedSong.cxx +++ b/src/DetachedSong.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/DetachedSong.hxx b/src/DetachedSong.hxx index 021b5de29..844283dcf 100644 --- a/src/DetachedSong.hxx +++ b/src/DetachedSong.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -32,9 +32,9 @@ struct LightSong; class Storage; +class Path; class DetachedSong { - friend DetachedSong map_song_detach(const LightSong &song); friend DetachedSong DatabaseDetachSong(const Storage &db, const LightSong &song); @@ -221,6 +221,11 @@ public: * @return true on success */ bool Update(); + + /** + * Load #tag and #mtime from a local file. + */ + bool LoadFile(Path path); }; #endif diff --git a/src/GlobalEvents.cxx b/src/GlobalEvents.cxx index 9c60f6357..8cbb85861 100644 --- a/src/GlobalEvents.cxx +++ b/src/GlobalEvents.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/GlobalEvents.hxx b/src/GlobalEvents.hxx index a9df03724..15ef58a89 100644 --- a/src/GlobalEvents.hxx +++ b/src/GlobalEvents.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/IOThread.cxx b/src/IOThread.cxx index e21ede4f3..654433c06 100644 --- a/src/IOThread.cxx +++ b/src/IOThread.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/IOThread.hxx b/src/IOThread.hxx index f6f5dffec..c7edd67b4 100644 --- a/src/IOThread.hxx +++ b/src/IOThread.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ class EventLoop; void -io_thread_init(void); +io_thread_init(); void io_thread_start(); @@ -36,7 +36,7 @@ io_thread_start(); * only. */ void -io_thread_run(void); +io_thread_run(); /** * Ask the I/O thread to quit, but does not wait for it. Usually, you @@ -44,10 +44,10 @@ io_thread_run(void); * includes this. */ void -io_thread_quit(void); +io_thread_quit(); void -io_thread_deinit(void); +io_thread_deinit(); gcc_const EventLoop & @@ -58,6 +58,6 @@ io_thread_get(); */ gcc_pure bool -io_thread_inside(void); +io_thread_inside(); #endif diff --git a/src/IcyMetaDataParser.cxx b/src/IcyMetaDataParser.cxx index 4c13c2c2c..9fb97d4d4 100644 --- a/src/IcyMetaDataParser.cxx +++ b/src/IcyMetaDataParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "tag/Tag.hxx" #include "tag/TagBuilder.hxx" #include "util/Domain.hxx" +#include "util/StringView.hxx" #include "Log.hxx" #include <assert.h> @@ -76,7 +77,7 @@ icy_add_item(TagBuilder &tag, TagType type, const char *value) } if (length > 0) - tag.AddItem(type, value, length); + tag.AddItem(type, {value, length}); } static void diff --git a/src/IcyMetaDataParser.hxx b/src/IcyMetaDataParser.hxx index 3075485b2..75aa1a9da 100644 --- a/src/IcyMetaDataParser.hxx +++ b/src/IcyMetaDataParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Idle.cxx b/src/Idle.cxx index 0b66065de..ee0e3c3da 100644 --- a/src/Idle.cxx +++ b/src/Idle.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Idle.hxx b/src/Idle.hxx index fb7150f98..cc89427ea 100644 --- a/src/Idle.hxx +++ b/src/Idle.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -78,13 +78,13 @@ idle_add(unsigned flags); * Atomically reads and resets the global idle flags value. */ unsigned -idle_get(void); +idle_get(); /** * Get idle names */ const char*const* -idle_get_names(void); +idle_get_names(); /** * Parse an idle name and return its mask. Returns 0 if the given diff --git a/src/Instance.cxx b/src/Instance.cxx index 232cd21df..77059c26c 100644 --- a/src/Instance.cxx +++ b/src/Instance.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "Partition.hxx" #include "Idle.hxx" #include "Stats.hxx" +#include "util/Error.hxx" #ifdef ENABLE_DATABASE #include "db/DatabaseError.hxx" @@ -76,7 +77,7 @@ Instance::OnDatabaseSongRemoved(const LightSong &song) #ifdef ENABLE_SQLITE /* if the song has a sticker, remove it */ if (sticker_enabled()) - sticker_song_delete(song); + sticker_song_delete(song, IgnoreError()); #endif const auto uri = song.GetURI(); diff --git a/src/Instance.hxx b/src/Instance.hxx index fa7711ab9..d8e485c74 100644 --- a/src/Instance.hxx +++ b/src/Instance.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Listen.cxx b/src/Listen.cxx index d48d795d1..cf4b41352 100644 --- a/src/Listen.cxx +++ b/src/Listen.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,9 +20,10 @@ #include "config.h" #include "Listen.hxx" #include "client/Client.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" +#include "net/SocketAddress.hxx" #include "event/ServerSocket.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -48,10 +49,9 @@ public: :ServerSocket(_loop), partition(_partition) {} private: - virtual void OnAccept(int fd, const sockaddr &address, - size_t address_length, int uid) { + void OnAccept(int fd, SocketAddress address, int uid) override { client_new(GetEventLoop(), partition, - fd, &address, address_length, uid); + fd, address, uid); } }; @@ -103,9 +103,9 @@ listen_systemd_activation(Error &error_r) bool listen_global_init(EventLoop &loop, Partition &partition, Error &error) { - int port = config_get_positive(CONF_PORT, DEFAULT_PORT); + int port = config_get_positive(ConfigOption::PORT, DEFAULT_PORT); const struct config_param *param = - config_get_param(CONF_BIND_TO_ADDRESS); + config_get_param(ConfigOption::BIND_TO_ADDRESS); listen_socket = new ClientListener(loop, partition); diff --git a/src/Listen.hxx b/src/Listen.hxx index d74c1d233..1e24d47f8 100644 --- a/src/Listen.hxx +++ b/src/Listen.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,6 +29,7 @@ extern int listen_port; bool listen_global_init(EventLoop &loop, Partition &partition, Error &error); -void listen_global_finish(void); +void +listen_global_finish(); #endif diff --git a/src/LocateUri.cxx b/src/LocateUri.cxx new file mode 100644 index 000000000..71c8d9093 --- /dev/null +++ b/src/LocateUri.cxx @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2003-2015 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 "LocateUri.hxx" +#include "client/Client.hxx" +#include "fs/AllocatedPath.hxx" +#include "ls.hxx" +#include "util/UriUtil.hxx" +#include "util/StringUtil.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +#ifdef ENABLE_DATABASE +#include "storage/StorageInterface.hxx" +#endif + +const Domain locate_uri_domain("locate_uri"); + +static LocatedUri +LocateFileUri(const char *uri, const Client *client, +#ifdef ENABLE_DATABASE + const Storage *storage, +#endif + Error &error) +{ + auto path = AllocatedPath::FromUTF8(uri, error); + if (path.IsNull()) + return LocatedUri::Unknown(); + +#ifdef ENABLE_DATABASE + if (storage != nullptr) { + const char *suffix = storage->MapToRelativeUTF8(uri); + if (suffix != nullptr) + /* this path was relative to the music + directory */ + return LocatedUri(LocatedUri::Type::RELATIVE, suffix); + } +#endif + + if (client != nullptr && !client->AllowFile(path, error)) + return LocatedUri::Unknown(); + + return LocatedUri(LocatedUri::Type::PATH, uri, std::move(path)); +} + +static LocatedUri +LocateAbsoluteUri(const char *uri, +#ifdef ENABLE_DATABASE + const Storage *storage, +#endif + Error &error) +{ + if (!uri_supported_scheme(uri)) { + error.Set(locate_uri_domain, "Unsupported URI scheme"); + return LocatedUri::Unknown(); + } + +#ifdef ENABLE_DATABASE + if (storage != nullptr) { + const char *suffix = storage->MapToRelativeUTF8(uri); + if (suffix != nullptr) + return LocatedUri(LocatedUri::Type::RELATIVE, suffix); + } +#endif + + return LocatedUri(LocatedUri::Type::ABSOLUTE, uri); +} + +LocatedUri +LocateUri(const char *uri, const Client *client, +#ifdef ENABLE_DATABASE + const Storage *storage, +#endif + Error &error) +{ + /* skip the obsolete "file://" prefix */ + const char *path_utf8 = StringAfterPrefix(uri, "file://"); + if (path_utf8 != nullptr) { + if (!PathTraitsUTF8::IsAbsolute(path_utf8)) { + error.Set(locate_uri_domain, "Malformed file:// URI"); + return LocatedUri::Unknown(); + } + + return LocateFileUri(path_utf8, client, +#ifdef ENABLE_DATABASE + storage, +#endif + error); + } else if (PathTraitsUTF8::IsAbsolute(uri)) + return LocateFileUri(uri, client, +#ifdef ENABLE_DATABASE + storage, +#endif + error); + else if (uri_has_scheme(uri)) + return LocateAbsoluteUri(uri, +#ifdef ENABLE_DATABASE + storage, +#endif + error); + else + return LocatedUri(LocatedUri::Type::RELATIVE, uri); +} diff --git a/src/LocateUri.hxx b/src/LocateUri.hxx new file mode 100644 index 000000000..1cb57ace4 --- /dev/null +++ b/src/LocateUri.hxx @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2003-2015 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_LOCATE_URI_HXX +#define MPD_LOCATE_URI_HXX + +#include "check.h" +#include "Compiler.h" +#include "fs/AllocatedPath.hxx" + +#ifdef WIN32 +#include <windows.h> +/* damn you, windows.h! */ +#ifdef ABSOLUTE +#undef ABSOLUTE +#endif +#ifdef RELATIVE +#undef RELATIVE +#endif +#endif + +class Domain; +class Error; +class Client; + +#ifdef ENABLE_DATABASE +class Storage; +#endif + +struct LocatedUri { + enum class Type { + /** + * Failed to parse the URI. + */ + UNKNOWN, + + /** + * An absolute URI with a supported scheme. + */ + ABSOLUTE, + + /** + * A relative URI path. + */ + RELATIVE, + + /** + * A local file. The #path attribute is valid. + */ + PATH, + } type; + + const char *canonical_uri; + + /** + * Contains the local file path if type==FILE. + */ + AllocatedPath path; + + LocatedUri(Type _type, const char *_uri, + AllocatedPath &&_path=AllocatedPath::Null()) + :type(_type), canonical_uri(_uri), path(std::move(_path)) {} + + gcc_const + static LocatedUri Unknown() { + return LocatedUri(Type::UNKNOWN, nullptr); + } + + bool IsUnknown() const { + return type == Type::UNKNOWN; + } +}; + +extern const Domain locate_uri_domain; + +/** + * Classify a URI. + * + * @param client the #Client that is used to determine whether a local + * file is allowed; nullptr disables the check and allows all local + * files + * @param storage a #Storage instance which may be used to convert + * absolute URIs to relative ones, using Storage::MapToRelativeUTF8(); + * that feature is disabled if this parameter is nullptr + */ +LocatedUri +LocateUri(const char *uri, const Client *client, +#ifdef ENABLE_DATABASE + const Storage *storage, +#endif + Error &error); + +#endif diff --git a/src/Log.cxx b/src/Log.cxx index ba691581b..585e51f7c 100644 --- a/src/Log.cxx +++ b/src/Log.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Log.hxx b/src/Log.hxx index 15077e374..684d5c394 100644 --- a/src/Log.hxx +++ b/src/Log.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/LogBackend.cxx b/src/LogBackend.cxx index 04c2e6324..b1b692cd3 100644 --- a/src/LogBackend.cxx +++ b/src/LogBackend.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,10 +23,6 @@ #include "util/Domain.hxx" #include "util/StringUtil.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <assert.h> #include <stdio.h> #include <string.h> @@ -65,10 +61,6 @@ ToAndroidLogLevel(LogLevel log_level) static LogLevel log_threshold = LogLevel::INFO; -#ifdef HAVE_GLIB -static const char *log_charset; -#endif - static bool enable_timestamp; #ifdef HAVE_SYSLOG @@ -81,16 +73,6 @@ SetLogThreshold(LogLevel _threshold) log_threshold = _threshold; } -#ifdef HAVE_GLIB - -void -SetLogCharset(const char *_charset) -{ - log_charset = _charset; -} - -#endif - void EnableLogTimestamp() { @@ -175,20 +157,6 @@ LogFinishSysLog() static void FileLog(const Domain &domain, const char *message) { -#ifdef HAVE_GLIB - char *converted; - - if (log_charset != nullptr) { - converted = g_convert_with_fallback(message, -1, - log_charset, "utf-8", - nullptr, nullptr, - nullptr, nullptr); - if (converted != nullptr) - message = converted; - } else - converted = nullptr; -#endif - fprintf(stderr, "%s%s: %.*s\n", enable_timestamp ? log_date() : "", domain.GetName(), @@ -199,10 +167,6 @@ FileLog(const Domain &domain, const char *message) to have an effect on WIN32 */ fflush(stderr); #endif - -#ifdef HAVE_GLIB - g_free(converted); -#endif } #endif /* !ANDROID */ diff --git a/src/LogBackend.hxx b/src/LogBackend.hxx index 23df2037e..d38d9eeb3 100644 --- a/src/LogBackend.hxx +++ b/src/LogBackend.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,13 +26,6 @@ void SetLogThreshold(LogLevel _threshold); -#ifdef HAVE_GLIB - -void -SetLogCharset(const char *_charset); - -#endif - void EnableLogTimestamp(); diff --git a/src/LogInit.cxx b/src/LogInit.cxx index 117c6d8dc..7f3c11b2e 100644 --- a/src/LogInit.cxx +++ b/src/LogInit.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "LogInit.hxx" #include "LogBackend.hxx" #include "Log.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "system/FatalError.hxx" @@ -31,10 +31,6 @@ #include "util/Domain.hxx" #include "system/FatalError.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <assert.h> #include <string.h> #include <fcntl.h> @@ -72,14 +68,14 @@ open_log_file(void) } static bool -log_init_file(unsigned line, Error &error) +log_init_file(int line, Error &error) { assert(!out_path.IsNull()); out_fd = open_log_file(); if (out_fd < 0) { const std::string out_path_utf8 = out_path.ToUTF8(); - error.FormatErrno("failed to open log file \"%s\" (config line %u)", + error.FormatErrno("failed to open log file \"%s\" (config line %d)", out_path_utf8.c_str(), line); return false; } @@ -89,7 +85,7 @@ log_init_file(unsigned line, Error &error) } static inline LogLevel -parse_log_level(const char *value, unsigned line) +parse_log_level(const char *value, int line) { if (0 == strcmp(value, "default")) return LogLevel::DEFAULT; @@ -98,7 +94,7 @@ parse_log_level(const char *value, unsigned line) else if (0 == strcmp(value, "verbose")) return LogLevel::DEBUG; else { - FormatFatalError("unknown log level \"%s\" at line %u", + FormatFatalError("unknown log level \"%s\" at line %d", value, line); } } @@ -131,22 +127,16 @@ log_init(bool verbose, bool use_stdout, Error &error) #else const struct config_param *param; -#ifdef HAVE_GLIB - const char *charset; - g_get_charset(&charset); - SetLogCharset(charset); -#endif - if (verbose) SetLogThreshold(LogLevel::DEBUG); - else if ((param = config_get_param(CONF_LOG_LEVEL)) != nullptr) + else if ((param = config_get_param(ConfigOption::LOG_LEVEL)) != nullptr) SetLogThreshold(parse_log_level(param->value.c_str(), param->line)); if (use_stdout) { return true; } else { - param = config_get_param(CONF_LOG_FILE); + param = config_get_param(ConfigOption::LOG_FILE); if (param == nullptr) { #ifdef HAVE_SYSLOG /* no configuration: default to syslog (if @@ -164,7 +154,7 @@ log_init(bool verbose, bool use_stdout, Error &error) return true; #endif } else { - out_path = config_get_path(CONF_LOG_FILE, error); + out_path = config_get_path(ConfigOption::LOG_FILE, error); return !out_path.IsNull() && log_init_file(param->line, error); } @@ -216,10 +206,6 @@ void setup_log_output(bool use_stdout) redirect_logs(out_fd); close(out_fd); out_fd = -1; - -#ifdef HAVE_GLIB - SetLogCharset(nullptr); -#endif #endif } diff --git a/src/LogInit.hxx b/src/LogInit.hxx index d0bcb40f2..30fcb8baa 100644 --- a/src/LogInit.hxx +++ b/src/LogInit.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -37,10 +37,11 @@ bool log_init(bool verbose, bool use_stdout, Error &error); void -log_deinit(void); +log_deinit(); void setup_log_output(bool use_stdout); -int cycle_log_files(void); +int +cycle_log_files(); #endif /* LOG_H */ diff --git a/src/LogLevel.hxx b/src/LogLevel.hxx index 2614a67d1..64c12e009 100644 --- a/src/LogLevel.hxx +++ b/src/LogLevel.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/LogV.hxx b/src/LogV.hxx index 6b16f82b4..7b108936d 100644 --- a/src/LogV.hxx +++ b/src/LogV.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Main.cxx b/src/Main.cxx index a3a1b0021..1a074d439 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ #include "PlaylistGlobal.hxx" #include "MusicChunk.hxx" #include "StateFile.hxx" -#include "PlayerThread.hxx" +#include "player/Thread.hxx" #include "Mapper.hxx" #include "Permission.hxx" #include "Listen.hxx" @@ -50,7 +50,6 @@ #include "AudioConfig.hxx" #include "pcm/PcmConvert.hxx" #include "unix/SignalHandlers.hxx" -#include "unix/Daemon.hxx" #include "system/FatalError.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -58,12 +57,16 @@ #include "thread/Slack.hxx" #include "lib/icu/Init.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigDefaults.hxx" #include "config/ConfigOption.hxx" #include "config/ConfigError.hxx" #include "Stats.hxx" +#ifdef ENABLE_DAEMON +#include "unix/Daemon.hxx" +#endif + #ifdef ENABLE_DATABASE #include "db/update/Service.hxx" #include "db/Configured.hxx" @@ -98,8 +101,8 @@ #include "org_musicpd_Bridge.h" #endif -#ifdef HAVE_GLIB -#include <glib.h> +#ifdef ENABLE_SYSTEMD_DAEMON +#include <systemd/sd-daemon.h> #endif #include <stdlib.h> @@ -130,17 +133,17 @@ Instance *instance; static StateFile *state_file; -#ifndef ANDROID +#ifdef ENABLE_DAEMON static bool glue_daemonize_init(const struct options *options, Error &error) { - auto pid_file = config_get_path(CONF_PID_FILE, error); + auto pid_file = config_get_path(ConfigOption::PID_FILE, error); if (pid_file.IsNull() && error.IsDefined()) return false; - daemonize_init(config_get_string(CONF_USER, nullptr), - config_get_string(CONF_GROUP, nullptr), + daemonize_init(config_get_string(ConfigOption::USER, nullptr), + config_get_string(ConfigOption::GROUP, nullptr), std::move(pid_file)); if (options->kill) @@ -154,7 +157,7 @@ glue_daemonize_init(const struct options *options, Error &error) static bool glue_mapper_init(Error &error) { - auto playlist_dir = config_get_path(CONF_PLAYLIST_DIR, error); + auto playlist_dir = config_get_path(ConfigOption::PLAYLIST_DIR, error); if (playlist_dir.IsNull() && error.IsDefined()) return false; @@ -249,7 +252,7 @@ glue_sticker_init(void) { #ifdef ENABLE_SQLITE Error error; - auto sticker_file = config_get_path(CONF_STICKER_FILE, error); + auto sticker_file = config_get_path(ConfigOption::STICKER_FILE, error); if (sticker_file.IsNull()) { if (error.IsDefined()) FatalError(error); @@ -264,7 +267,7 @@ glue_sticker_init(void) static bool glue_state_file_init(Error &error) { - auto path_fs = config_get_path(CONF_STATE_FILE, error); + auto path_fs = config_get_path(ConfigOption::STATE_FILE, error); if (path_fs.IsNull()) { if (error.IsDefined()) return false; @@ -280,8 +283,9 @@ glue_state_file_init(Error &error) #endif } - unsigned interval = config_get_unsigned(CONF_STATE_FILE_INTERVAL, - StateFile::DEFAULT_INTERVAL); + const unsigned interval = + config_get_unsigned(ConfigOption::STATE_FILE_INTERVAL, + StateFile::DEFAULT_INTERVAL); state_file = new StateFile(std::move(path_fs), interval, *instance->partition, @@ -318,7 +322,7 @@ initialize_decoder_and_player(void) const struct config_param *param; size_t buffer_size; - param = config_get_param(CONF_AUDIO_BUFFER_SIZE); + param = config_get_param(ConfigOption::AUDIO_BUFFER_SIZE); if (param != nullptr) { char *test; long tmp = strtol(param->value.c_str(), &test, 10); @@ -339,7 +343,7 @@ initialize_decoder_and_player(void) (unsigned long)buffer_size); float perc; - param = config_get_param(CONF_BUFFER_BEFORE_PLAY); + param = config_get_param(ConfigOption::BUFFER_BEFORE_PLAY); if (param != nullptr) { char *test; perc = strtod(param->value.c_str(), &test); @@ -357,7 +361,7 @@ initialize_decoder_and_player(void) buffered_before_play = buffered_chunks; const unsigned max_length = - config_get_positive(CONF_MAX_PLAYLIST_LENGTH, + config_get_positive(ConfigOption::MAX_PLAYLIST_LENGTH, DEFAULT_PLAYLIST_MAX_LENGTH); instance->partition = new Partition(*instance, @@ -419,21 +423,15 @@ int mpd_main(int argc, char *argv[]) struct options options; Error error; -#ifndef ANDROID +#ifdef ENABLE_DAEMON daemonize_close_stdin(); +#endif +#ifndef ANDROID #ifdef HAVE_LOCALE_H /* initialize locale */ setlocale(LC_CTYPE,""); -#endif - -#ifdef HAVE_GLIB - g_set_application_name("Music Player Daemon"); - -#if !GLIB_CHECK_VERSION(2,32,0) - /* enable GLib's thread safety code */ - g_thread_init(nullptr); -#endif + setlocale(LC_COLLATE, ""); #endif #endif @@ -467,7 +465,9 @@ int mpd_main(int argc, char *argv[]) LogError(error); return EXIT_FAILURE; } +#endif +#ifdef ENABLE_DAEMON if (!glue_daemonize_init(&options, error)) { LogError(error); return EXIT_FAILURE; @@ -498,7 +498,8 @@ int mpd_main(int argc, char *argv[]) } #endif - const unsigned max_clients = config_get_positive(CONF_MAX_CONN, 10); + const unsigned max_clients = + config_get_positive(ConfigOption::MAX_CONN, 10); instance->client_list = new ClientList(max_clients); initialize_decoder_and_player(); @@ -509,7 +510,7 @@ int mpd_main(int argc, char *argv[]) return EXIT_FAILURE; } -#ifndef ANDROID +#ifdef ENABLE_DAEMON daemonize_set_user(); daemonize_begin(options.daemon); #endif @@ -541,7 +542,10 @@ static int mpd_main_after_fork(struct options options) GlobalEvents::Register(GlobalEvents::SHUTDOWN, shutdown_event_emitted); #endif - ConfigureFS(); + if (!ConfigureFS(error)) { + LogError(error); + return EXIT_FAILURE; + } if (!glue_mapper_init(error)) { LogError(error); @@ -582,9 +586,11 @@ static int mpd_main_after_fork(struct options options) playlist_list_global_init(); -#ifndef ANDROID +#ifdef ENABLE_DAEMON daemonize_commit(); +#endif +#ifndef ANDROID setup_log_output(options.log_stderr); SignalHandlersInit(*instance->event_loop); @@ -620,17 +626,17 @@ static int mpd_main_after_fork(struct options options) instance->partition->outputs.SetReplayGainMode(replay_gain_get_real_mode(instance->partition->playlist.queue.random)); #ifdef ENABLE_DATABASE - if (config_get_bool(CONF_AUTO_UPDATE, false)) { + if (config_get_bool(ConfigOption::AUTO_UPDATE, false)) { #ifdef ENABLE_INOTIFY if (instance->storage != nullptr && instance->update != nullptr) mpd_inotify_init(*instance->event_loop, *instance->storage, *instance->update, - config_get_unsigned(CONF_AUTO_UPDATE_DEPTH, + config_get_unsigned(ConfigOption::AUTO_UPDATE_DEPTH, INT_MAX)); #else - FormatWarning(main_domain, + FormatWarning(config_domain, "inotify: auto_update was disabled. enable during compilation phase"); #endif } @@ -650,6 +656,10 @@ static int mpd_main_after_fork(struct options options) a huge value to allow the kernel to reduce CPU wakeups */ SetThreadTimerSlackMS(100); +#ifdef ENABLE_SYSTEMD_DAEMON + sd_notify(0, "READY=1"); +#endif + /* run the main loop */ instance->event_loop->Run(); @@ -707,6 +717,8 @@ static int mpd_main_after_fork(struct options options) mapper_finish(); #endif + DeinitFS(); + delete instance->partition; command_finish(); decoder_plugin_deinit_all(); @@ -721,9 +733,11 @@ static int mpd_main_after_fork(struct options options) delete instance->event_loop; delete instance; instance = nullptr; -#ifndef ANDROID + +#ifdef ENABLE_DAEMON daemonize_finish(); #endif + #ifdef WIN32 WSACleanup(); #endif diff --git a/src/Main.hxx b/src/Main.hxx index 7e3fecd0b..0229b788c 100644 --- a/src/Main.hxx +++ b/src/Main.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -61,7 +61,7 @@ win32_main(int argc, char *argv[]); * This function should be called just before entering main loop. */ void -win32_app_started(void); +win32_app_started(); /** * When running as a service reports to service control manager @@ -71,7 +71,7 @@ win32_app_started(void); * This function should be called just after leaving main loop. */ void -win32_app_stopping(void); +win32_app_stopping(); #endif diff --git a/src/Mapper.cxx b/src/Mapper.cxx index 7baad9459..15d706922 100644 --- a/src/Mapper.cxx +++ b/src/Mapper.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -59,7 +59,8 @@ mapper_init(AllocatedPath &&_playlist_dir) mapper_set_playlist_dir(std::move(_playlist_dir)); } -void mapper_finish(void) +void +mapper_finish() { } @@ -86,9 +87,9 @@ map_uri_fs(const char *uri) } std::string -map_fs_to_utf8(const char *path_fs) +map_fs_to_utf8(Path path_fs) { - if (PathTraitsFS::IsSeparator(path_fs[0])) { + if (path_fs.IsAbsolute()) { if (instance->storage == nullptr) return std::string(); @@ -96,18 +97,20 @@ map_fs_to_utf8(const char *path_fs) if (music_dir_fs.IsNull()) return std::string(); - path_fs = music_dir_fs.RelativeFS(path_fs); - if (path_fs == nullptr || *path_fs == 0) + auto relative = music_dir_fs.Relative(path_fs); + if (relative == nullptr || *relative == 0) return std::string(); + + path_fs = Path::FromFS(relative); } - return PathToUTF8(path_fs); + return path_fs.ToUTF8(); } #endif const AllocatedPath & -map_spl_path(void) +map_spl_path() { return playlist_dir_fs; } diff --git a/src/Mapper.hxx b/src/Mapper.hxx index 7ff41f239..ca5d29810 100644 --- a/src/Mapper.hxx +++ b/src/Mapper.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -30,12 +30,14 @@ #define PLAYLIST_FILE_SUFFIX ".m3u" +class Path; class AllocatedPath; void mapper_init(AllocatedPath &&playlist_dir); -void mapper_finish(void); +void +mapper_finish(); #ifdef ENABLE_DATABASE @@ -58,7 +60,7 @@ map_uri_fs(const char *uri); */ gcc_pure std::string -map_fs_to_utf8(const char *path_fs); +map_fs_to_utf8(Path path_fs); #endif @@ -67,7 +69,7 @@ map_fs_to_utf8(const char *path_fs); */ gcc_const const AllocatedPath & -map_spl_path(void); +map_spl_path(); /** * Maps a playlist name (without the ".m3u" suffix) to a file system diff --git a/src/MixRampInfo.hxx b/src/MixRampInfo.hxx index 90c2c984a..297adfe07 100644 --- a/src/MixRampInfo.hxx +++ b/src/MixRampInfo.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/MusicBuffer.cxx b/src/MusicBuffer.cxx index 709b40413..5de94508a 100644 --- a/src/MusicBuffer.cxx +++ b/src/MusicBuffer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/MusicBuffer.hxx b/src/MusicBuffer.hxx index cf7c90f91..60b792f3a 100644 --- a/src/MusicBuffer.hxx +++ b/src/MusicBuffer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/MusicChunk.cxx b/src/MusicChunk.cxx index 3cfd232c0..9f09b35c5 100644 --- a/src/MusicChunk.cxx +++ b/src/MusicChunk.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/MusicChunk.hxx b/src/MusicChunk.hxx index 805112d02..0f750f049 100644 --- a/src/MusicChunk.hxx +++ b/src/MusicChunk.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -119,13 +119,10 @@ struct MusicChunk { * where you may write into. After you are finished, call * Expand(). * - * @param chunk the MusicChunk object - * @param audio_format the audio format for the appended data; + * @param af the audio format for the appended data; * must stay the same for the life cycle of this chunk * @param data_time the time within the song * @param bit_rate the current bit rate of the source file - * @param max_length_r the maximum write length is returned - * here * @return a writable buffer, or nullptr if the chunk is full */ WritableBuffer<void> Write(AudioFormat af, @@ -136,8 +133,7 @@ struct MusicChunk { * Increases the length of the chunk after the caller has written to * the buffer returned by Write(). * - * @param chunk the MusicChunk object - * @param audio_format the audio format for the appended data; must + * @param af the audio format for the appended data; must * stay the same for the life cycle of this chunk * @param length the number of bytes which were appended * @return true if the chunk is full diff --git a/src/MusicPipe.cxx b/src/MusicPipe.cxx index 43ce2dbb2..cd874eb33 100644 --- a/src/MusicPipe.cxx +++ b/src/MusicPipe.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/MusicPipe.hxx b/src/MusicPipe.hxx index 4f29d0728..078530abb 100644 --- a/src/MusicPipe.hxx +++ b/src/MusicPipe.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Partition.cxx b/src/Partition.cxx index de1170557..1d48fefdb 100644 --- a/src/Partition.cxx +++ b/src/Partition.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "Partition.hxx" +#include "Instance.hxx" #include "DetachedSong.hxx" #include "output/MultipleOutputs.hxx" #include "mixer/Volume.hxx" @@ -27,6 +28,12 @@ #ifdef ENABLE_DATABASE +const Database * +Partition::GetDatabase(Error &error) const +{ + return instance.GetDatabase(error); +} + void Partition::DatabaseModified(const Database &db) { diff --git a/src/Partition.hxx b/src/Partition.hxx index d89c4b41f..cd7b3c66e 100644 --- a/src/Partition.hxx +++ b/src/Partition.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,8 +23,8 @@ #include "queue/Playlist.hxx" #include "output/MultipleOutputs.hxx" #include "mixer/Listener.hxx" -#include "PlayerControl.hxx" -#include "PlayerListener.hxx" +#include "player/Control.hxx" +#include "player/Listener.hxx" #include "Chrono.hxx" #include "Compiler.h" @@ -177,6 +177,13 @@ struct Partition final : private PlayerListener, private MixerListener { #ifdef ENABLE_DATABASE /** + * Returns the global #Database instance. May return nullptr + * if this MPD configuration has no database (no + * music_directory was configured). + */ + const Database *GetDatabase(Error &error) const; + + /** * The database has been modified. Propagate the change to * all subsystems. */ diff --git a/src/Permission.cxx b/src/Permission.cxx index d6c267ab7..b6ff39bdb 100644 --- a/src/Permission.cxx +++ b/src/Permission.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "Permission.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "system/FatalError.hxx" @@ -92,7 +92,7 @@ void initPermissions(void) permission_default = PERMISSION_READ | PERMISSION_ADD | PERMISSION_CONTROL | PERMISSION_ADMIN; - param = config_get_param(CONF_PASSWORD); + param = config_get_param(ConfigOption::PASSWORD); if (param) { permission_default = 0; @@ -118,7 +118,7 @@ void initPermissions(void) } while ((param = param->next) != nullptr); } - param = config_get_param(CONF_DEFAULT_PERMS); + param = config_get_param(ConfigOption::DEFAULT_PERMS); if (param) permission_default = parsePermissions(param->value.c_str()); diff --git a/src/Permission.hxx b/src/Permission.hxx index 60761c696..50edc1da4 100644 --- a/src/Permission.hxx +++ b/src/Permission.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,8 +28,10 @@ static constexpr unsigned PERMISSION_ADMIN = 8; int getPermissionFromPassword(char const* password, unsigned* permission); -unsigned getDefaultPermissions(void); +unsigned +getDefaultPermissions(); -void initPermissions(void); +void +initPermissions(); #endif diff --git a/src/PlaylistDatabase.cxx b/src/PlaylistDatabase.cxx index 3421ecb02..75cb699b8 100644 --- a/src/PlaylistDatabase.cxx +++ b/src/PlaylistDatabase.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlaylistDatabase.hxx b/src/PlaylistDatabase.hxx index 17f82f64b..078530c75 100644 --- a/src/PlaylistDatabase.hxx +++ b/src/PlaylistDatabase.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlaylistError.cxx b/src/PlaylistError.cxx index 085246f15..d069dd3a3 100644 --- a/src/PlaylistError.cxx +++ b/src/PlaylistError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlaylistError.hxx b/src/PlaylistError.hxx index 0f2424f41..500acd711 100644 --- a/src/PlaylistError.hxx +++ b/src/PlaylistError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index ab269378a..9d2b56eae 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,12 +20,15 @@ #include "config.h" #include "PlaylistFile.hxx" #include "PlaylistSave.hxx" +#include "PlaylistError.hxx" #include "db/PlaylistInfo.hxx" #include "db/PlaylistVector.hxx" #include "DetachedSong.hxx" #include "SongLoader.hxx" #include "Mapper.hxx" #include "fs/io/TextFile.hxx" +#include "fs/io/FileOutputStream.hxx" +#include "fs/io/BufferedOutputStream.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "config/ConfigDefaults.hxx" @@ -35,7 +38,9 @@ #include "fs/Traits.hxx" #include "fs/Charset.hxx" #include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "fs/DirectoryReader.hxx" +#include "util/Macros.hxx" #include "util/StringUtil.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -53,11 +58,12 @@ bool playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS; void spl_global_init(void) { - playlist_max_length = config_get_positive(CONF_MAX_PLAYLIST_LENGTH, - DEFAULT_PLAYLIST_MAX_LENGTH); + playlist_max_length = + config_get_positive(ConfigOption::MAX_PLAYLIST_LENGTH, + DEFAULT_PLAYLIST_MAX_LENGTH); playlist_saveAbsolutePaths = - config_get_bool(CONF_SAVE_ABSOLUTE_PATHS, + config_get_bool(ConfigOption::SAVE_ABSOLUTE_PATHS, DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS); } @@ -106,7 +112,7 @@ spl_check_name(const char *name_utf8, Error &error) return true; } -static AllocatedPath +AllocatedPath spl_map_to_fs(const char *name_utf8, Error &error) { if (spl_map(error).IsNull() || !spl_check_name(name_utf8, error)) @@ -133,7 +139,7 @@ IsNotFoundError(const Error &error) #endif } -static void +void TranslatePlaylistError(Error &error) { if (IsNotFoundError(error)) { @@ -166,29 +172,28 @@ LoadPlaylistFileInfo(PlaylistInfo &info, const Path parent_path_fs, const Path name_fs) { - const char *name_fs_str = name_fs.c_str(); - size_t name_length = strlen(name_fs_str); - - if (name_length < sizeof(PLAYLIST_FILE_SUFFIX) || - memchr(name_fs_str, '\n', name_length) != nullptr) + if (name_fs.HasNewline()) return false; - if (!StringEndsWith(name_fs_str, PLAYLIST_FILE_SUFFIX)) + const auto *const name_fs_str = name_fs.c_str(); + const auto *const name_fs_end = + FindStringSuffix(name_fs_str, + PATH_LITERAL(PLAYLIST_FILE_SUFFIX)); + if (name_fs_end == nullptr) return false; - const auto path_fs = AllocatedPath::Build(parent_path_fs, name_fs); - struct stat st; - if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode)) + FileInfo fi; + if (!GetFileInfo(AllocatedPath::Build(parent_path_fs, name_fs), fi) || + !fi.IsRegular()) return false; - std::string name(name_fs_str, - name_length + 1 - sizeof(PLAYLIST_FILE_SUFFIX)); + PathTraitsFS::string name(name_fs_str, name_fs_end); std::string name_utf8 = PathToUTF8(name.c_str()); if (name_utf8.empty()) return false; info.name = std::move(name_utf8); - info.mtime = st.st_mtime; + info.mtime = fi.GetModificationTime(); return true; } @@ -223,24 +228,22 @@ SavePlaylistFile(const PlaylistFileContents &contents, const char *utf8path, { assert(utf8path != nullptr); - if (spl_map(error).IsNull()) - return false; - const auto path_fs = spl_map_to_fs(utf8path, error); if (path_fs.IsNull()) return false; - FILE *file = FOpen(path_fs, FOpenMode::WriteText); - if (file == nullptr) { - playlist_errno(error); + FileOutputStream fos(path_fs, error); + if (!fos.IsDefined()) { + TranslatePlaylistError(error); return false; } + BufferedOutputStream bos(fos); + for (const auto &uri_utf8 : contents) - playlist_print_uri(file, uri_utf8.c_str()); + playlist_print_uri(bos, uri_utf8.c_str()); - fclose(file); - return true; + return bos.Flush(error) && fos.Commit(error); } PlaylistFileContents @@ -248,9 +251,6 @@ LoadPlaylistFile(const char *utf8path, Error &error) { PlaylistFileContents contents; - if (spl_map(error).IsNull()) - return contents; - const auto path_fs = spl_map_to_fs(utf8path, error); if (path_fs.IsNull()) return contents; @@ -266,18 +266,28 @@ LoadPlaylistFile(const char *utf8path, Error &error) if (*s == 0 || *s == PLAYLIST_COMMENT) continue; +#ifdef _UNICODE + wchar_t buffer[MAX_PATH]; + auto result = MultiByteToWideChar(CP_ACP, 0, s, -1, + buffer, ARRAY_SIZE(buffer)); + if (result <= 0) + continue; + + const Path path = Path::FromFS(buffer); +#else + const Path path = Path::FromFS(s); +#endif + std::string uri_utf8; if (!uri_has_scheme(s)) { #ifdef ENABLE_DATABASE - uri_utf8 = map_fs_to_utf8(s); + uri_utf8 = map_fs_to_utf8(path); if (uri_utf8.empty()) { - if (PathTraitsFS::IsAbsolute(s)) { - uri_utf8 = PathToUTF8(s); + if (path.IsAbsolute()) { + uri_utf8 = path.ToUTF8(); if (uri_utf8.empty()) continue; - - uri_utf8.insert(0, "file://"); } else continue; } @@ -285,7 +295,7 @@ LoadPlaylistFile(const char *utf8path, Error &error) continue; #endif } else { - uri_utf8 = PathToUTF8(s); + uri_utf8 = path.ToUTF8(); if (uri_utf8.empty()) continue; } @@ -333,9 +343,6 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest, bool spl_clear(const char *utf8path, Error &error) { - if (spl_map(error).IsNull()) - return false; - const auto path_fs = spl_map_to_fs(utf8path, error); if (path_fs.IsNull()) return false; @@ -392,36 +399,28 @@ spl_remove_index(const char *utf8path, unsigned pos, Error &error) bool spl_append_song(const char *utf8path, const DetachedSong &song, Error &error) { - if (spl_map(error).IsNull()) - return false; - const auto path_fs = spl_map_to_fs(utf8path, error); if (path_fs.IsNull()) return false; - FILE *file = FOpen(path_fs, FOpenMode::AppendText); - if (file == nullptr) { - playlist_errno(error); - return false; - } - - struct stat st; - if (fstat(fileno(file), &st) < 0) { - playlist_errno(error); - fclose(file); + AppendFileOutputStream fos(path_fs, error); + if (!fos.IsDefined()) { + TranslatePlaylistError(error); return false; } - if (st.st_size / off_t(MPD_PATH_MAX + 1) >= (off_t)playlist_max_length) { - fclose(file); + if (fos.Tell() / (MPD_PATH_MAX + 1) >= playlist_max_length) { error.Set(playlist_domain, int(PlaylistResult::TOO_LARGE), "Stored playlist is too large"); return false; } - playlist_print_song(file, song); + BufferedOutputStream bos(fos); - fclose(file); + playlist_print_song(bos, song); + + if (!bos.Flush(error) || !fos.Commit(error)) + return false; idle_add(IDLE_STORED_PLAYLIST); return true; @@ -469,9 +468,6 @@ spl_rename_internal(Path from_path_fs, Path to_path_fs, bool spl_rename(const char *utf8from, const char *utf8to, Error &error) { - if (spl_map(error).IsNull()) - return false; - const auto from_path_fs = spl_map_to_fs(utf8from, error); if (from_path_fs.IsNull()) return false; diff --git a/src/PlaylistFile.hxx b/src/PlaylistFile.hxx index 7154b1f84..5f905480f 100644 --- a/src/PlaylistFile.hxx +++ b/src/PlaylistFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ class DetachedSong; class SongLoader; class PlaylistVector; class Error; +class AllocatedPath; typedef std::vector<std::string> PlaylistFileContents; @@ -36,7 +37,7 @@ extern bool playlist_saveAbsolutePaths; * Perform some global initialization, e.g. load configuration values. */ void -spl_global_init(void); +spl_global_init(); /** * Determines whether the specified string is a valid name for a @@ -45,6 +46,12 @@ spl_global_init(void); bool spl_valid_name(const char *name_utf8); +AllocatedPath +spl_map_to_fs(const char *name_utf8, Error &error); + +void +TranslatePlaylistError(Error &error); + /** * Returns a list of stored_playlist_info struct pointers. Returns * nullptr if an error occurred. diff --git a/src/PlaylistGlobal.cxx b/src/PlaylistGlobal.cxx index dacfad0c7..fb65843f5 100644 --- a/src/PlaylistGlobal.cxx +++ b/src/PlaylistGlobal.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlaylistGlobal.hxx b/src/PlaylistGlobal.hxx index a2e3bb030..244eed702 100644 --- a/src/PlaylistGlobal.hxx +++ b/src/PlaylistGlobal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlaylistPrint.cxx b/src/PlaylistPrint.cxx index cfa56c7b3..80f348710 100644 --- a/src/PlaylistPrint.cxx +++ b/src/PlaylistPrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include "Instance.hxx" #include "db/Interface.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "input/InputStream.hxx" #include "DetachedSong.hxx" #include "fs/Traits.hxx" @@ -37,15 +38,17 @@ #define SONG_TIME "Time: " void -playlist_print_uris(Client &client, const playlist &playlist) +playlist_print_uris(Response &r, Partition &partition, + const playlist &playlist) { const Queue &queue = playlist.queue; - queue_print_uris(client, queue, 0, queue.GetLength()); + queue_print_uris(r, partition, queue, 0, queue.GetLength()); } bool -playlist_print_info(Client &client, const playlist &playlist, +playlist_print_info(Response &r, Partition &partition, + const playlist &playlist, unsigned start, unsigned end) { const Queue &queue = playlist.queue; @@ -58,12 +61,12 @@ playlist_print_info(Client &client, const playlist &playlist, /* an invalid "start" offset is fatal */ return false; - queue_print_info(client, queue, start, end); + queue_print_info(r, partition, queue, start, end); return true; } bool -playlist_print_id(Client &client, const playlist &playlist, +playlist_print_id(Response &r, Partition &partition, const playlist &playlist, unsigned id) { int position; @@ -73,50 +76,53 @@ playlist_print_id(Client &client, const playlist &playlist, /* no such song */ return false; - return playlist_print_info(client, playlist, position, position + 1); + return playlist_print_info(r, partition, + playlist, position, position + 1); } bool -playlist_print_current(Client &client, const playlist &playlist) +playlist_print_current(Response &r, Partition &partition, + const playlist &playlist) { int current_position = playlist.GetCurrentPosition(); if (current_position < 0) return false; - queue_print_info(client, playlist.queue, + queue_print_info(r, partition, playlist.queue, current_position, current_position + 1); return true; } void -playlist_print_find(Client &client, const playlist &playlist, +playlist_print_find(Response &r, Partition &partition, + const playlist &playlist, const SongFilter &filter) { - queue_find(client, playlist.queue, filter); + queue_find(r, partition, playlist.queue, filter); } void -playlist_print_changes_info(Client &client, +playlist_print_changes_info(Response &r, Partition &partition, const playlist &playlist, uint32_t version) { - queue_print_changes_info(client, playlist.queue, version); + queue_print_changes_info(r, partition, playlist.queue, version); } void -playlist_print_changes_position(Client &client, +playlist_print_changes_position(Response &r, const playlist &playlist, uint32_t version) { - queue_print_changes_position(client, playlist.queue, version); + queue_print_changes_position(r, playlist.queue, version); } #ifdef ENABLE_DATABASE static bool -PrintSongDetails(Client &client, const char *uri_utf8) +PrintSongDetails(Response &r, Partition &partition, const char *uri_utf8) { - const Database *db = client.partition.instance.database; + const Database *db = partition.instance.database; if (db == nullptr) return false; @@ -124,7 +130,7 @@ PrintSongDetails(Client &client, const char *uri_utf8) if (song == nullptr) return false; - song_print_info(client, *song); + song_print_info(r, partition, *song); db->ReturnSong(song); return true; } @@ -132,10 +138,12 @@ PrintSongDetails(Client &client, const char *uri_utf8) #endif bool -spl_print(Client &client, const char *name_utf8, bool detail, +spl_print(Response &r, Partition &partition, + const char *name_utf8, bool detail, Error &error) { #ifndef ENABLE_DATABASE + (void)partition; (void)detail; #endif @@ -145,10 +153,10 @@ spl_print(Client &client, const char *name_utf8, bool detail, for (const auto &uri_utf8 : contents) { #ifdef ENABLE_DATABASE - if (!detail || !PrintSongDetails(client, uri_utf8.c_str())) + if (!detail || !PrintSongDetails(r, partition, + uri_utf8.c_str())) #endif - client_printf(client, SONG_FILE "%s\n", - uri_utf8.c_str()); + r.Format(SONG_FILE "%s\n", uri_utf8.c_str()); } return true; diff --git a/src/PlaylistPrint.hxx b/src/PlaylistPrint.hxx index 38a4cc7cf..bc4c2cb47 100644 --- a/src/PlaylistPrint.hxx +++ b/src/PlaylistPrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,15 +23,18 @@ #include <stdint.h> struct playlist; +struct Partition; class SongFilter; class Client; +class Response; class Error; /** * Sends the whole playlist to the client, song URIs only. */ void -playlist_print_uris(Client &client, const playlist &playlist); +playlist_print_uris(Response &r, Partition &partition, + const playlist &playlist); /** * Sends a range of the playlist to the client, including all known @@ -40,7 +43,8 @@ playlist_print_uris(Client &client, const playlist &playlist); * This function however fails when the start offset is invalid. */ bool -playlist_print_info(Client &client, const playlist &playlist, +playlist_print_info(Response &r, Partition &partition, + const playlist &playlist, unsigned start, unsigned end); /** @@ -49,8 +53,8 @@ playlist_print_info(Client &client, const playlist &playlist, * @return true on suite, false if there is no such song */ bool -playlist_print_id(Client &client, const playlist &playlist, - unsigned id); +playlist_print_id(Response &r, Partition &partition, + const playlist &playlist, unsigned id); /** * Sends the current song to the client. @@ -58,20 +62,22 @@ playlist_print_id(Client &client, const playlist &playlist, * @return true on success, false if there is no current song */ bool -playlist_print_current(Client &client, const playlist &playlist); +playlist_print_current(Response &r, Partition &partition, + const playlist &playlist); /** * Find songs in the playlist. */ void -playlist_print_find(Client &client, const playlist &playlist, +playlist_print_find(Response &r, Partition &partition, + const playlist &playlist, const SongFilter &filter); /** * Print detailed changes since the specified playlist version. */ void -playlist_print_changes_info(Client &client, +playlist_print_changes_info(Response &r, Partition &partition, const playlist &playlist, uint32_t version); @@ -79,7 +85,7 @@ playlist_print_changes_info(Client &client, * Print changes since the specified playlist version, position only. */ void -playlist_print_changes_position(Client &client, +playlist_print_changes_position(Response &r, const playlist &playlist, uint32_t version); @@ -92,7 +98,8 @@ playlist_print_changes_position(Client &client, * @return true on success, false if the playlist does not exist */ bool -spl_print(Client &client, const char *name_utf8, bool detail, +spl_print(Response &r, Partition &partition, + const char *name_utf8, bool detail, Error &error); #endif diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx index 67f05267f..a2f48ba54 100644 --- a/src/PlaylistSave.cxx +++ b/src/PlaylistSave.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,6 +29,9 @@ #include "fs/AllocatedPath.hxx" #include "fs/Traits.hxx" #include "fs/FileSystem.hxx" +#include "fs/NarrowPath.hxx" +#include "fs/io/FileOutputStream.hxx" +#include "fs/io/BufferedOutputStream.hxx" #include "util/Alloc.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -37,7 +40,7 @@ #include <string.h> void -playlist_print_song(FILE *file, const DetachedSong &song) +playlist_print_song(BufferedOutputStream &os, const DetachedSong &song) { const char *uri_utf8 = playlist_saveAbsolutePaths ? song.GetRealURI() @@ -45,11 +48,11 @@ playlist_print_song(FILE *file, const DetachedSong &song) const auto uri_fs = AllocatedPath::FromUTF8(uri_utf8); if (!uri_fs.IsNull()) - fprintf(file, "%s\n", uri_fs.c_str()); + os.Format("%s\n", NarrowPath(uri_fs).c_str()); } void -playlist_print_uri(FILE *file, const char *uri) +playlist_print_uri(BufferedOutputStream &os, const char *uri) { auto path = #ifdef ENABLE_DATABASE @@ -61,41 +64,43 @@ playlist_print_uri(FILE *file, const char *uri) AllocatedPath::FromUTF8(uri); if (!path.IsNull()) - fprintf(file, "%s\n", path.c_str()); + os.Format("%s\n", NarrowPath(path).c_str()); } -PlaylistResult -spl_save_queue(const char *name_utf8, const Queue &queue) +bool +spl_save_queue(const char *name_utf8, const Queue &queue, Error &error) { - if (map_spl_path().IsNull()) - return PlaylistResult::DISABLED; - - if (!spl_valid_name(name_utf8)) - return PlaylistResult::BAD_NAME; - - const auto path_fs = map_spl_utf8_to_fs(name_utf8); + const auto path_fs = spl_map_to_fs(name_utf8, error); if (path_fs.IsNull()) - return PlaylistResult::BAD_NAME; + return false; - if (FileExists(path_fs)) - return PlaylistResult::LIST_EXISTS; + if (FileExists(path_fs)) { + error.Set(playlist_domain, int(PlaylistResult::LIST_EXISTS), + "Playlist already exists"); + return false; + } - FILE *file = FOpen(path_fs, FOpenMode::WriteText); + FileOutputStream fos(path_fs, error); + if (!fos.IsDefined()) { + TranslatePlaylistError(error); + return false; + } - if (file == nullptr) - return PlaylistResult::ERRNO; + BufferedOutputStream bos(fos); for (unsigned i = 0; i < queue.GetLength(); i++) - playlist_print_song(file, queue.Get(i)); + playlist_print_song(bos, queue.Get(i)); - fclose(file); + if (!bos.Flush(error) || !fos.Commit(error)) + return false; idle_add(IDLE_STORED_PLAYLIST); - return PlaylistResult::SUCCESS; + return true; } -PlaylistResult -spl_save_playlist(const char *name_utf8, const playlist &playlist) +bool +spl_save_playlist(const char *name_utf8, const playlist &playlist, + Error &error) { - return spl_save_queue(name_utf8, playlist.queue); + return spl_save_queue(name_utf8, playlist.queue, error); } diff --git a/src/PlaylistSave.hxx b/src/PlaylistSave.hxx index 914c8c086..536c5c8d1 100644 --- a/src/PlaylistSave.hxx +++ b/src/PlaylistSave.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,32 +20,29 @@ #ifndef MPD_PLAYLIST_SAVE_H #define MPD_PLAYLIST_SAVE_H -#include "PlaylistError.hxx" - -#include <stdio.h> - struct Queue; struct playlist; -struct PlayerControl; +class BufferedOutputStream; class DetachedSong; class Error; void -playlist_print_song(FILE *file, const DetachedSong &song); +playlist_print_song(BufferedOutputStream &os, const DetachedSong &song); void -playlist_print_uri(FILE *fp, const char *uri); +playlist_print_uri(BufferedOutputStream &os, const char *uri); /** * Saves a queue object into a stored playlist file. */ -PlaylistResult -spl_save_queue(const char *name_utf8, const Queue &queue); +bool +spl_save_queue(const char *name_utf8, const Queue &queue, Error &error); /** * Saves a playlist object into a stored playlist file. */ -PlaylistResult -spl_save_playlist(const char *name_utf8, const playlist &playlist); +bool +spl_save_playlist(const char *name_utf8, const playlist &playlist, + Error &error); #endif diff --git a/src/ReplayGainConfig.cxx b/src/ReplayGainConfig.cxx index c3bbcac3c..b8a907cdc 100644 --- a/src/ReplayGainConfig.cxx +++ b/src/ReplayGainConfig.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #include "config.h" #include "ReplayGainConfig.hxx" #include "Idle.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "system/FatalError.hxx" @@ -81,7 +81,8 @@ replay_gain_set_mode_string(const char *p) void replay_gain_global_init(void) { - const struct config_param *param = config_get_param(CONF_REPLAYGAIN); + const struct config_param *param = + config_get_param(ConfigOption::REPLAYGAIN); if (param != nullptr && !replay_gain_set_mode_string(param->value.c_str())) { @@ -89,7 +90,7 @@ void replay_gain_global_init(void) param->value.c_str(), param->line); } - param = config_get_param(CONF_REPLAYGAIN_PREAMP); + param = config_get_param(ConfigOption::REPLAYGAIN_PREAMP); if (param) { char *test; @@ -110,7 +111,7 @@ void replay_gain_global_init(void) replay_gain_preamp = pow(10, f / 20.0); } - param = config_get_param(CONF_REPLAYGAIN_MISSING_PREAMP); + param = config_get_param(ConfigOption::REPLAYGAIN_MISSING_PREAMP); if (param) { char *test; @@ -131,7 +132,8 @@ void replay_gain_global_init(void) replay_gain_missing_preamp = pow(10, f / 20.0); } - replay_gain_limit = config_get_bool(CONF_REPLAYGAIN_LIMIT, DEFAULT_REPLAYGAIN_LIMIT); + replay_gain_limit = config_get_bool(ConfigOption::REPLAYGAIN_LIMIT, + DEFAULT_REPLAYGAIN_LIMIT); } ReplayGainMode diff --git a/src/ReplayGainConfig.hxx b/src/ReplayGainConfig.hxx index e498a56dd..986506d8b 100644 --- a/src/ReplayGainConfig.hxx +++ b/src/ReplayGainConfig.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,14 +29,15 @@ extern float replay_gain_preamp; extern float replay_gain_missing_preamp; extern bool replay_gain_limit; -void replay_gain_global_init(void); +void +replay_gain_global_init(); /** * Returns the current replay gain mode as a machine-readable string. */ gcc_pure const char * -replay_gain_get_mode_string(void); +replay_gain_get_mode_string(); /** * Sets the replay gain mode, parsed from a string. diff --git a/src/ReplayGainInfo.cxx b/src/ReplayGainInfo.cxx index 580773521..98aa3360d 100644 --- a/src/ReplayGainInfo.cxx +++ b/src/ReplayGainInfo.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/ReplayGainInfo.hxx b/src/ReplayGainInfo.hxx index 37815c933..c94cb1fa8 100644 --- a/src/ReplayGainInfo.hxx +++ b/src/ReplayGainInfo.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -39,8 +39,7 @@ struct ReplayGainTuple { peak = 0.0; } - gcc_pure - bool IsDefined() const { + constexpr bool IsDefined() const { return gain > -100; } @@ -52,6 +51,11 @@ struct ReplayGainTuple { struct ReplayGainInfo { ReplayGainTuple tuples[2]; + constexpr bool IsDefined() const { + return tuples[REPLAY_GAIN_ALBUM].IsDefined() || + tuples[REPLAY_GAIN_TRACK].IsDefined(); + } + void Clear() { tuples[REPLAY_GAIN_ALBUM].Clear(); tuples[REPLAY_GAIN_TRACK].Clear(); diff --git a/src/SongFilter.cxx b/src/SongFilter.cxx index dc0a63df3..4fc7145da 100644 --- a/src/SongFilter.cxx +++ b/src/SongFilter.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,12 +23,12 @@ #include "DetachedSong.hxx" #include "tag/Tag.hxx" #include "util/ConstBuffer.hxx" +#include "util/StringAPI.hxx" #include "util/ASCII.hxx" #include "util/UriUtil.hxx" #include "lib/icu/Collate.hxx" #include <assert.h> -#include <string.h> #include <stdlib.h> #define LOCATE_TAG_FILE_KEY "file" @@ -55,12 +55,12 @@ locate_parse_type(const char *str) } gcc_pure -static std::string +static AllocatedString<> ImportString(const char *p, bool fold_case) { return fold_case ? IcuCaseFold(p) - : std::string(p); + : AllocatedString<>::Duplicate(p); } SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case) @@ -70,7 +70,7 @@ SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case) } SongFilter::Item::Item(unsigned _tag, time_t _time) - :tag(_tag), time(_time) + :tag(_tag), value(nullptr), time(_time) { } @@ -82,11 +82,14 @@ SongFilter::Item::StringMatch(const char *s) const assert(s != nullptr); #endif + assert(tag != LOCATE_TAG_MODIFIED_SINCE); + if (fold_case) { - const std::string folded = IcuCaseFold(s); - return folded.find(value) != folded.npos; + const auto folded = IcuCaseFold(s); + assert(!folded.IsNull()); + return StringFind(folded.c_str(), value.c_str()) != nullptr; } else { - return s == value; + return StringIsEqual(s, value.c_str()); } } @@ -300,12 +303,12 @@ SongFilter::HasOtherThanBase() const return false; } -std::string +const char * SongFilter::GetBase() const { for (const auto &i : items) if (i.GetTag() == LOCATE_TAG_BASE_TYPE) return i.GetValue(); - return std::string(); + return nullptr; } diff --git a/src/SongFilter.hxx b/src/SongFilter.hxx index f51bd85c6..e15f0338f 100644 --- a/src/SongFilter.hxx +++ b/src/SongFilter.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,10 +20,10 @@ #ifndef MPD_SONG_FILTER_HXX #define MPD_SONG_FILTER_HXX +#include "util/AllocatedString.hxx" #include "Compiler.h" #include <list> -#include <string> #include <stdint.h> #include <time.h> @@ -51,7 +51,7 @@ public: bool fold_case; - std::string value; + AllocatedString<> value; /** * For #LOCATE_TAG_MODIFIED_SINCE @@ -76,8 +76,8 @@ public: return fold_case; } - const std::string &GetValue() const { - return value; + const char *GetValue() const { + return value.c_str(); } gcc_pure gcc_nonnull(2) @@ -149,11 +149,11 @@ public: bool HasOtherThanBase() const; /** - * Returns the "base" specification (if there is one) or an - * empty string. + * Returns the "base" specification (if there is one) or + * nullptr. */ gcc_pure - std::string GetBase() const; + const char *GetBase() const; }; /** diff --git a/src/SongLoader.cxx b/src/SongLoader.cxx index 43e57e93b..14f9f4dd6 100644 --- a/src/SongLoader.cxx +++ b/src/SongLoader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,19 +19,15 @@ #include "config.h" #include "SongLoader.hxx" +#include "LocateUri.hxx" #include "client/Client.hxx" #include "db/DatabaseSong.hxx" #include "storage/StorageInterface.hxx" -#include "ls.hxx" -#include "fs/AllocatedPath.hxx" -#include "fs/Traits.hxx" -#include "util/UriUtil.hxx" #include "util/Error.hxx" #include "DetachedSong.hxx" #include "PlaylistError.hxx" #include <assert.h> -#include <string.h> #ifdef ENABLE_DATABASE @@ -42,7 +38,22 @@ SongLoader::SongLoader(const Client &_client) #endif DetachedSong * -SongLoader::LoadFile(const char *path_utf8, Error &error) const +SongLoader::LoadFromDatabase(const char *uri, Error &error) const +{ +#ifdef ENABLE_DATABASE + if (db != nullptr) + return DatabaseDetachSong(*db, *storage, uri, error); +#else + (void)uri; +#endif + + error.Set(playlist_domain, int(PlaylistResult::NO_SUCH_SONG), + "No database"); + return nullptr; +} + +DetachedSong * +SongLoader::LoadFile(const char *path_utf8, Path path_fs, Error &error) const { #ifdef ENABLE_DATABASE if (storage != nullptr) { @@ -50,21 +61,12 @@ SongLoader::LoadFile(const char *path_utf8, Error &error) const if (suffix != nullptr) /* this path was relative to the music directory - obtain it from the database */ - return LoadSong(suffix, error); + return LoadFromDatabase(suffix, error); } #endif - if (client != nullptr) { - const auto path_fs = AllocatedPath::FromUTF8(path_utf8, error); - if (path_fs.IsNull()) - return nullptr; - - if (!client->AllowFile(path_fs, error)) - return nullptr; - } - DetachedSong *song = new DetachedSong(path_utf8); - if (!song->Update()) { + if (!song->LoadFile(path_fs)) { error.Set(playlist_domain, int(PlaylistResult::NO_SUCH_SONG), "No such file"); delete song; @@ -75,6 +77,27 @@ SongLoader::LoadFile(const char *path_utf8, Error &error) const } DetachedSong * +SongLoader::LoadSong(const LocatedUri &located_uri, Error &error) const +{ + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + gcc_unreachable(); + + case LocatedUri::Type::ABSOLUTE: + return new DetachedSong(located_uri.canonical_uri); + + case LocatedUri::Type::RELATIVE: + return LoadFromDatabase(located_uri.canonical_uri, error); + + case LocatedUri::Type::PATH: + return LoadFile(located_uri.canonical_uri, located_uri.path, + error); + } + + gcc_unreachable(); +} + +DetachedSong * SongLoader::LoadSong(const char *uri_utf8, Error &error) const { #if !CLANG_CHECK_VERSION(3,6) @@ -82,33 +105,13 @@ SongLoader::LoadSong(const char *uri_utf8, Error &error) const assert(uri_utf8 != nullptr); #endif - if (memcmp(uri_utf8, "file:///", 8) == 0) - /* absolute path */ - return LoadFile(uri_utf8 + 7, error); - else if (PathTraitsUTF8::IsAbsolute(uri_utf8)) - /* absolute path */ - return LoadFile(uri_utf8, error); - else if (uri_has_scheme(uri_utf8)) { - /* remove URI */ - if (!uri_supported_scheme(uri_utf8)) { - error.Set(playlist_domain, - int(PlaylistResult::NO_SUCH_SONG), - "Unsupported URI scheme"); - return nullptr; - } - - return new DetachedSong(uri_utf8); - } else { - /* URI relative to the music directory */ - + const auto located_uri = LocateUri(uri_utf8, client, #ifdef ENABLE_DATABASE - if (db != nullptr) - return DatabaseDetachSong(*db, *storage, - uri_utf8, error); + storage, #endif - - error.Set(playlist_domain, int(PlaylistResult::NO_SUCH_SONG), - "No database"); + error); + if (located_uri.IsUnknown()) return nullptr; - } + + return LoadSong(located_uri, error); } diff --git a/src/SongLoader.hxx b/src/SongLoader.hxx index 229703972..1c1300f11 100644 --- a/src/SongLoader.hxx +++ b/src/SongLoader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,7 +29,9 @@ class Client; class Database; class Storage; class DetachedSong; +class Path; class Error; +struct LocatedUri; /** * A utility class that loads a #DetachedSong object by its URI. If @@ -66,12 +68,18 @@ public: } #endif + DetachedSong *LoadSong(const LocatedUri &uri, Error &error) const; + gcc_nonnull_all DetachedSong *LoadSong(const char *uri_utf8, Error &error) const; private: gcc_nonnull_all - DetachedSong *LoadFile(const char *path_utf8, Error &error) const; + DetachedSong *LoadFromDatabase(const char *uri, Error &error) const; + + gcc_nonnull_all + DetachedSong *LoadFile(const char *path_utf8, Path path_fs, + Error &error) const; }; #endif diff --git a/src/SongPrint.cxx b/src/SongPrint.cxx index 05d462b6d..804920f9b 100644 --- a/src/SongPrint.cxx +++ b/src/SongPrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,18 +20,20 @@ #include "config.h" #include "SongPrint.hxx" #include "db/LightSong.hxx" +#include "Partition.hxx" +#include "Instance.hxx" #include "storage/StorageInterface.hxx" #include "DetachedSong.hxx" #include "TimePrint.hxx" #include "TagPrint.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" #include "fs/Traits.hxx" #include "util/UriUtil.hxx" #define SONG_FILE "file: " static void -song_print_uri(Client &client, const char *uri, bool base) +song_print_uri(Response &r, Partition &partition, const char *uri, bool base) { std::string allocated; @@ -39,12 +41,14 @@ song_print_uri(Client &client, const char *uri, bool base) uri = PathTraitsUTF8::GetBase(uri); } else { #ifdef ENABLE_DATABASE - const Storage *storage = client.GetStorage(); + const Storage *storage = partition.instance.storage; if (storage != nullptr) { const char *suffix = storage->MapToRelativeUTF8(uri); if (suffix != nullptr) uri = suffix; } +#else + (void)partition; #endif allocated = uri_remove_auth(uri); @@ -52,75 +56,81 @@ song_print_uri(Client &client, const char *uri, bool base) uri = allocated.c_str(); } - client_printf(client, "%s%s\n", SONG_FILE, uri); + r.Format(SONG_FILE "%s\n", uri); } void -song_print_uri(Client &client, const LightSong &song, bool base) +song_print_uri(Response &r, Partition &partition, + const LightSong &song, bool base) { - if (!base && song.directory != nullptr) { - client_printf(client, "%s%s/%s\n", SONG_FILE, - song.directory, song.uri); - } else - song_print_uri(client, song.uri, base); + if (!base && song.directory != nullptr) + r.Format(SONG_FILE "%s/%s\n", song.directory, song.uri); + else + song_print_uri(r, partition, song.uri, base); } void -song_print_uri(Client &client, const DetachedSong &song, bool base) +song_print_uri(Response &r, Partition &partition, + const DetachedSong &song, bool base) { - song_print_uri(client, song.GetURI(), base); + song_print_uri(r, partition, song.GetURI(), base); } void -song_print_info(Client &client, const LightSong &song, bool base) +song_print_info(Response &r, Partition &partition, + const LightSong &song, bool base) { - song_print_uri(client, song, base); + song_print_uri(r, partition, song, base); const unsigned start_ms = song.start_time.ToMS(); const unsigned end_ms = song.end_time.ToMS(); if (end_ms > 0) - client_printf(client, "Range: %u.%03u-%u.%03u\n", - start_ms / 1000, - start_ms % 1000, - end_ms / 1000, - end_ms % 1000); + r.Format("Range: %u.%03u-%u.%03u\n", + start_ms / 1000, + start_ms % 1000, + end_ms / 1000, + end_ms % 1000); else if (start_ms > 0) - client_printf(client, "Range: %u.%03u-\n", - start_ms / 1000, - start_ms % 1000); + r.Format("Range: %u.%03u-\n", + start_ms / 1000, + start_ms % 1000); if (song.mtime > 0) - time_print(client, "Last-Modified", song.mtime); + time_print(r, "Last-Modified", song.mtime); - tag_print(client, *song.tag); + tag_print(r, *song.tag); } void -song_print_info(Client &client, const DetachedSong &song, bool base) +song_print_info(Response &r, Partition &partition, + const DetachedSong &song, bool base) { - song_print_uri(client, song, base); + song_print_uri(r, partition, song, base); const unsigned start_ms = song.GetStartTime().ToMS(); const unsigned end_ms = song.GetEndTime().ToMS(); if (end_ms > 0) - client_printf(client, "Range: %u.%03u-%u.%03u\n", - start_ms / 1000, - start_ms % 1000, - end_ms / 1000, - end_ms % 1000); + r.Format("Range: %u.%03u-%u.%03u\n", + start_ms / 1000, + start_ms % 1000, + end_ms / 1000, + end_ms % 1000); else if (start_ms > 0) - client_printf(client, "Range: %u.%03u-\n", - start_ms / 1000, - start_ms % 1000); + r.Format("Range: %u.%03u-\n", + start_ms / 1000, + start_ms % 1000); if (song.GetLastModified() > 0) - time_print(client, "Last-Modified", song.GetLastModified()); + time_print(r, "Last-Modified", song.GetLastModified()); - tag_print_values(client, song.GetTag()); + tag_print_values(r, song.GetTag()); const auto duration = song.GetDuration(); if (!duration.IsNegative()) - client_printf(client, "Time: %u\n", duration.RoundS()); + r.Format("Time: %i\n" + "duration: %1.3f\n", + duration.RoundS(), + duration.ToDoubleS()); } diff --git a/src/SongPrint.hxx b/src/SongPrint.hxx index 5e4c93a74..50be70fa8 100644 --- a/src/SongPrint.hxx +++ b/src/SongPrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,18 +22,23 @@ struct LightSong; class DetachedSong; -class Client; +class Response; +struct Partition; void -song_print_info(Client &client, const DetachedSong &song, bool base=false); +song_print_info(Response &r, Partition &partition, + const DetachedSong &song, bool base=false); void -song_print_info(Client &client, const LightSong &song, bool base=false); +song_print_info(Response &r, Partition &partition, + const LightSong &song, bool base=false); void -song_print_uri(Client &client, const LightSong &song, bool base=false); +song_print_uri(Response &r, Partition &partition, + const LightSong &song, bool base=false); void -song_print_uri(Client &client, const DetachedSong &song, bool base=false); +song_print_uri(Response &r, Partition &partition, + const DetachedSong &song, bool base=false); #endif diff --git a/src/SongSave.cxx b/src/SongSave.cxx index 895e9805b..8cf3a17ba 100644 --- a/src/SongSave.cxx +++ b/src/SongSave.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/SongSave.hxx b/src/SongSave.hxx index 28c217249..318f3cf33 100644 --- a/src/SongSave.hxx +++ b/src/SongSave.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx index 0245b9117..cf90fb9a2 100644 --- a/src/SongUpdate.cxx +++ b/src/SongUpdate.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #include "util/Error.hxx" #include "fs/AllocatedPath.hxx" #include "fs/Traits.hxx" -#include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "decoder/DecoderList.hxx" #include "tag/Tag.hxx" #include "tag/TagBuilder.hxx" @@ -37,6 +37,10 @@ #include "TagFile.hxx" #include "TagStream.hxx" +#ifdef ENABLE_ARCHIVE +#include "TagArchive.hxx" +#endif + #include <assert.h> #include <string.h> #include <sys/stat.h> @@ -52,9 +56,13 @@ Song::LoadFile(Storage &storage, const char *path_utf8, Directory &parent) Song *song = NewFile(path_utf8, parent); //in archive ? - bool success = parent.device == DEVICE_INARCHIVE + bool success = +#ifdef ENABLE_ARCHIVE + parent.device == DEVICE_INARCHIVE ? song->UpdateFileInArchive(storage) - : song->UpdateFile(storage); + : +#endif + song->UpdateFile(storage); if (!success) { song->Free(); return nullptr; @@ -83,7 +91,7 @@ Song::UpdateFile(Storage &storage) { const auto &relative_uri = GetURI(); - FileInfo info; + StorageFileInfo info; if (!storage.GetInfo(relative_uri.c_str(), true, info, IgnoreError())) return false; @@ -113,6 +121,10 @@ Song::UpdateFile(Storage &storage) return true; } +#endif + +#ifdef ENABLE_ARCHIVE + bool Song::UpdateFileInArchive(const Storage &storage) { @@ -132,7 +144,7 @@ Song::UpdateFileInArchive(const Storage &storage) return false; TagBuilder tag_builder; - if (!tag_stream_scan(path_fs.c_str(), full_tag_handler, &tag_builder)) + if (!tag_archive_scan(path_fs, full_tag_handler, &tag_builder)) return false; tag_builder.Commit(tag); @@ -142,27 +154,35 @@ Song::UpdateFileInArchive(const Storage &storage) #endif bool +DetachedSong::LoadFile(Path path) +{ + FileInfo fi; + if (!GetFileInfo(path, fi) || !fi.IsRegular()) + return false; + + TagBuilder tag_builder; + if (!tag_file_scan(path, full_tag_handler, &tag_builder)) + return false; + + if (tag_builder.IsEmpty()) + tag_scan_fallback(path, &full_tag_handler, + &tag_builder); + + mtime = fi.GetModificationTime(); + tag_builder.Commit(tag); + return true; +} + +bool DetachedSong::Update() { if (IsAbsoluteFile()) { const AllocatedPath path_fs = AllocatedPath::FromUTF8(GetRealURI()); - - struct stat st; - if (!StatFile(path_fs, st) || !S_ISREG(st.st_mode)) - return false; - - TagBuilder tag_builder; - if (!tag_file_scan(path_fs, full_tag_handler, &tag_builder)) + if (path_fs.IsNull()) return false; - if (tag_builder.IsEmpty()) - tag_scan_fallback(path_fs, &full_tag_handler, - &tag_builder); - - mtime = st.st_mtime; - tag_builder.Commit(tag); - return true; + return LoadFile(path_fs); } else if (IsRemote()) { TagBuilder tag_builder; if (!tag_stream_scan(uri.c_str(), full_tag_handler, diff --git a/src/StateFile.cxx b/src/StateFile.cxx index 7e9e35cc3..ebf6cf576 100644 --- a/src/StateFile.cxx +++ b/src/StateFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/StateFile.hxx b/src/StateFile.hxx index 15ba13b97..b96b89dfb 100644 --- a/src/StateFile.hxx +++ b/src/StateFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/Stats.cxx b/src/Stats.cxx index 39d371ace..0f92b4f47 100644 --- a/src/Stats.cxx +++ b/src/Stats.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,8 @@ #include "config.h" #include "Stats.hxx" -#include "PlayerControl.hxx" -#include "client/Client.hxx" +#include "player/Control.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "db/Selection.hxx" @@ -94,7 +94,7 @@ stats_update(const Database &db) } static void -db_stats_print(Client &client, const Database &db) +db_stats_print(Response &r, const Database &db) { if (!stats_update(db)) return; @@ -102,41 +102,38 @@ db_stats_print(Client &client, const Database &db) unsigned total_duration_s = std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count(); - client_printf(client, - "artists: %u\n" - "albums: %u\n" - "songs: %u\n" - "db_playtime: %u\n", - stats.artist_count, - stats.album_count, - stats.song_count, - total_duration_s); + r.Format("artists: %u\n" + "albums: %u\n" + "songs: %u\n" + "db_playtime: %u\n", + stats.artist_count, + stats.album_count, + stats.song_count, + total_duration_s); const time_t update_stamp = db.GetUpdateStamp(); if (update_stamp > 0) - client_printf(client, - "db_update: %lu\n", - (unsigned long)update_stamp); + r.Format("db_update: %lu\n", + (unsigned long)update_stamp); } #endif void -stats_print(Client &client) +stats_print(Response &r, const Partition &partition) { - client_printf(client, - "uptime: %u\n" - "playtime: %lu\n", + r.Format("uptime: %u\n" + "playtime: %lu\n", #ifdef WIN32 - GetProcessUptimeS(), + GetProcessUptimeS(), #else - MonotonicClockS() - start_time, + MonotonicClockS() - start_time, #endif - (unsigned long)(client.player_control.GetTotalPlayTime() + 0.5)); + (unsigned long)(partition.pc.GetTotalPlayTime() + 0.5)); #ifdef ENABLE_DATABASE - const Database *db = client.partition.instance.database; + const Database *db = partition.instance.database; if (db != nullptr) - db_stats_print(client, *db); + db_stats_print(r, *db); #endif } diff --git a/src/Stats.hxx b/src/Stats.hxx index 0d36ec0b2..879e1b7be 100644 --- a/src/Stats.hxx +++ b/src/Stats.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,14 +20,16 @@ #ifndef MPD_STATS_HXX #define MPD_STATS_HXX -class Client; +class Response; +struct Partition; -void stats_global_init(void); +void +stats_global_init(); void stats_invalidate(); void -stats_print(Client &client); +stats_print(Response &r, const Partition &partition); #endif diff --git a/src/TagArchive.cxx b/src/TagArchive.cxx new file mode 100644 index 000000000..49f66d8f5 --- /dev/null +++ b/src/TagArchive.cxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2015 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 "TagArchive.hxx" +#include "TagStream.hxx" +#include "fs/Path.hxx" +#include "util/Error.hxx" +#include "input/InputStream.hxx" +#include "input/plugins/ArchiveInputPlugin.hxx" +#include "thread/Cond.hxx" + +#include <assert.h> + +bool +tag_archive_scan(Path path, const tag_handler &handler, void *handler_ctx) +{ + assert(!path.IsNull()); + + Mutex mutex; + Cond cond; + auto *is = OpenArchiveInputStream(path, mutex, cond, IgnoreError()); + if (is == nullptr) + return false; + + bool result = tag_stream_scan(*is, handler, handler_ctx); + delete is; + return result; +} diff --git a/src/TagArchive.hxx b/src/TagArchive.hxx new file mode 100644 index 000000000..f14d892ff --- /dev/null +++ b/src/TagArchive.hxx @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2003-2015 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_TAG_ARCHIVE_HXX +#define MPD_TAG_ARCHIVE_HXX + +#include "check.h" + +class Path; +struct tag_handler; + +/** + * Scan the tags of a song file inside an archive. Invokes matching + * decoder plugins, but does not invoke the special "APE" and "ID3" + * scanners. + * + * @return true if the file was recognized (even if no metadata was + * found) + */ +bool +tag_archive_scan(Path path, const tag_handler &handler, void *handler_ctx); + +#endif diff --git a/src/TagFile.cxx b/src/TagFile.cxx index 7655b96ff..0640c132e 100644 --- a/src/TagFile.cxx +++ b/src/TagFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -87,11 +87,13 @@ tag_file_scan(Path path_fs, const tag_handler &handler, void *handler_ctx) /* check if there's a suffix and a plugin */ - const char *suffix = uri_get_suffix(path_fs.c_str()); + const auto *suffix = path_fs.GetSuffix(); if (suffix == nullptr) return false; - TagFileScan tfs(path_fs, suffix, handler, handler_ctx); + const auto suffix_utf8 = Path::FromFS(suffix).ToUTF8(); + + TagFileScan tfs(path_fs, suffix_utf8.c_str(), handler, handler_ctx); return decoder_plugins_try([&](const DecoderPlugin &plugin){ return tfs.Scan(plugin); }); diff --git a/src/TagFile.hxx b/src/TagFile.hxx index b11a8ac1c..6c2c5b4bf 100644 --- a/src/TagFile.hxx +++ b/src/TagFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/TagPrint.cxx b/src/TagPrint.cxx index 4937fa622..d938d8fa5 100644 --- a/src/TagPrint.cxx +++ b/src/TagPrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,40 +20,38 @@ #include "config.h" #include "TagPrint.hxx" #include "tag/Tag.hxx" -#include "tag/TagSettings.h" -#include "client/Client.hxx" +#include "tag/Settings.hxx" +#include "client/Response.hxx" -#define SONG_TIME "Time: " - -void tag_print_types(Client &client) +void +tag_print_types(Response &r) { - int i; - - for (i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) { - if (!ignore_tag_items[i]) - client_printf(client, "tagtype: %s\n", - tag_item_names[i]); - } + for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; i++) + if (IsTagEnabled(i)) + r.Format("tagtype: %s\n", tag_item_names[i]); } void -tag_print(Client &client, TagType type, const char *value) +tag_print(Response &r, TagType type, const char *value) { - client_printf(client, "%s: %s\n", tag_item_names[type], value); + r.Format("%s: %s\n", tag_item_names[type], value); } void -tag_print_values(Client &client, const Tag &tag) +tag_print_values(Response &r, const Tag &tag) { for (const auto &i : tag) - client_printf(client, "%s: %s\n", - tag_item_names[i.type], i.value); + r.Format("%s: %s\n", tag_item_names[i.type], i.value); } -void tag_print(Client &client, const Tag &tag) +void +tag_print(Response &r, const Tag &tag) { if (!tag.duration.IsNegative()) - client_printf(client, SONG_TIME "%i\n", tag.duration.RoundS()); + r.Format("Time: %i\n" + "duration: %1.3f\n", + tag.duration.RoundS(), + tag.duration.ToDoubleS()); - tag_print_values(client, tag); + tag_print_values(r, tag); } diff --git a/src/TagPrint.hxx b/src/TagPrint.hxx index 6675bb7d8..30405638e 100644 --- a/src/TagPrint.hxx +++ b/src/TagPrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,17 +25,18 @@ enum TagType : uint8_t; struct Tag; -class Client; +class Response; -void tag_print_types(Client &client); +void +tag_print_types(Response &response); void -tag_print(Client &client, TagType type, const char *value); +tag_print(Response &response, TagType type, const char *value); void -tag_print_values(Client &client, const Tag &tag); +tag_print_values(Response &response, const Tag &tag); void -tag_print(Client &client, const Tag &tag); +tag_print(Response &response, const Tag &tag); #endif diff --git a/src/TagSave.cxx b/src/TagSave.cxx index 107aca7db..e81de8dfd 100644 --- a/src/TagSave.cxx +++ b/src/TagSave.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/TagSave.hxx b/src/TagSave.hxx index fd4b91f98..9b71849b9 100644 --- a/src/TagSave.hxx +++ b/src/TagSave.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/TagStream.cxx b/src/TagStream.cxx index 6201028f6..dd5d8f7ec 100644 --- a/src/TagStream.cxx +++ b/src/TagStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/TagStream.hxx b/src/TagStream.hxx index 71dd71ff7..e1bd25b56 100644 --- a/src/TagStream.hxx +++ b/src/TagStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/TimePrint.cxx b/src/TimePrint.cxx index 5526ec7d6..f9a4dbd94 100644 --- a/src/TimePrint.cxx +++ b/src/TimePrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,10 +19,10 @@ #include "config.h" #include "TimePrint.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" void -time_print(Client &client, const char *name, time_t t) +time_print(Response &r, const char *name, time_t t) { #ifdef WIN32 const struct tm *tm2 = gmtime(&t); @@ -41,5 +41,5 @@ time_print(Client &client, const char *name, time_t t) "%FT%TZ", #endif tm2); - client_printf(client, "%s: %s\n", name, buffer); + r.Format("%s: %s\n", name, buffer); } diff --git a/src/TimePrint.hxx b/src/TimePrint.hxx index afdb3c1c9..6ded9cca0 100644 --- a/src/TimePrint.hxx +++ b/src/TimePrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,12 +22,12 @@ #include <time.h> -class Client; +class Response; /** * Write a line with a time stamp to the client. */ void -time_print(Client &client, const char *name, time_t t); +time_print(Response &r, const char *name, time_t t); #endif diff --git a/src/android/Context.cxx b/src/android/Context.cxx index f75e1503e..eba00363c 100644 --- a/src/android/Context.cxx +++ b/src/android/Context.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/android/Context.hxx b/src/android/Context.hxx index b8a47777d..0dd1cc124 100644 --- a/src/android/Context.hxx +++ b/src/android/Context.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/android/Environment.cxx b/src/android/Environment.cxx index 9813b0b79..29b2a9841 100644 --- a/src/android/Environment.cxx +++ b/src/android/Environment.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/android/Environment.hxx b/src/android/Environment.hxx index 5a54ea361..c2e5ff3bd 100644 --- a/src/android/Environment.hxx +++ b/src/android/Environment.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchiveDomain.cxx b/src/archive/ArchiveDomain.cxx index 4adf4a886..801b3879a 100644 --- a/src/archive/ArchiveDomain.cxx +++ b/src/archive/ArchiveDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchiveDomain.hxx b/src/archive/ArchiveDomain.hxx index 817ae5835..50ac7235e 100644 --- a/src/archive/ArchiveDomain.hxx +++ b/src/archive/ArchiveDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchiveFile.hxx b/src/archive/ArchiveFile.hxx index 473eef70b..2afd9f7f7 100644 --- a/src/archive/ArchiveFile.hxx +++ b/src/archive/ArchiveFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchiveList.cxx b/src/archive/ArchiveList.cxx index 79c3a16fe..709db4dc0 100644 --- a/src/archive/ArchiveList.cxx +++ b/src/archive/ArchiveList.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,13 +29,13 @@ #include <string.h> const ArchivePlugin *const archive_plugins[] = { -#ifdef HAVE_BZ2 +#ifdef ENABLE_BZ2 &bz2_archive_plugin, #endif -#ifdef HAVE_ZZIP +#ifdef ENABLE_ZZIP &zzip_archive_plugin, #endif -#ifdef HAVE_ISO9660 +#ifdef ENABLE_ISO9660 &iso9660_archive_plugin, #endif nullptr diff --git a/src/archive/ArchiveList.hxx b/src/archive/ArchiveList.hxx index 1f1b0ae96..73d17dd13 100644 --- a/src/archive/ArchiveList.hxx +++ b/src/archive/ArchiveList.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -39,9 +39,11 @@ const ArchivePlugin * archive_plugin_from_name(const char *name); /* this is where we "load" all the "plugins" ;-) */ -void archive_plugin_init_all(void); +void +archive_plugin_init_all(); /* this is where we "unload" all the "plugins" */ -void archive_plugin_deinit_all(void); +void +archive_plugin_deinit_all(); #endif diff --git a/src/archive/ArchiveLookup.cxx b/src/archive/ArchiveLookup.cxx index 53730c504..78e2c48d0 100644 --- a/src/archive/ArchiveLookup.cxx +++ b/src/archive/ArchiveLookup.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchiveLookup.hxx b/src/archive/ArchiveLookup.hxx index 0c08951a9..4154e4e2f 100644 --- a/src/archive/ArchiveLookup.hxx +++ b/src/archive/ArchiveLookup.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchivePlugin.cxx b/src/archive/ArchivePlugin.cxx index 67f469e08..ff16c0dd5 100644 --- a/src/archive/ArchivePlugin.cxx +++ b/src/archive/ArchivePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/ArchivePlugin.hxx b/src/archive/ArchivePlugin.hxx index eb24bbdf9..eaf2c83c1 100644 --- a/src/archive/ArchivePlugin.hxx +++ b/src/archive/ArchivePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -32,13 +32,13 @@ struct ArchivePlugin { * have/need one this must false if there is an error and * true otherwise */ - bool (*init)(void); + bool (*init)(); /** * optional, set this to nullptr if the archive plugin doesn't * have/need one */ - void (*finish)(void); + void (*finish)(); /** * tryes to open archive file and associates handle with archive diff --git a/src/archive/ArchiveVisitor.hxx b/src/archive/ArchiveVisitor.hxx index 6759695ca..c0b09da28 100644 --- a/src/archive/ArchiveVisitor.hxx +++ b/src/archive/ArchiveVisitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/plugins/Bzip2ArchivePlugin.cxx b/src/archive/plugins/Bzip2ArchivePlugin.cxx index 2b92049dd..3b40e0b36 100644 --- a/src/archive/plugins/Bzip2ArchivePlugin.cxx +++ b/src/archive/plugins/Bzip2ArchivePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -53,7 +53,7 @@ public: Bzip2ArchiveFile(Path path, InputStream *_is) :ArchiveFile(bz2_archive_plugin), - name(PathTraitsFS::GetBase(path.c_str())), + name(path.GetBase().c_str()), istream(_is) { // remove .bz2 suffix const size_t len = name.length(); diff --git a/src/archive/plugins/Bzip2ArchivePlugin.hxx b/src/archive/plugins/Bzip2ArchivePlugin.hxx index 1a0a578d1..6bb439d7a 100644 --- a/src/archive/plugins/Bzip2ArchivePlugin.hxx +++ b/src/archive/plugins/Bzip2ArchivePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/plugins/Iso9660ArchivePlugin.cxx b/src/archive/plugins/Iso9660ArchivePlugin.cxx index ba415d3c5..e25cd0af7 100644 --- a/src/archive/plugins/Iso9660ArchivePlugin.cxx +++ b/src/archive/plugins/Iso9660ArchivePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/plugins/Iso9660ArchivePlugin.hxx b/src/archive/plugins/Iso9660ArchivePlugin.hxx index 9335e83b3..baec9c407 100644 --- a/src/archive/plugins/Iso9660ArchivePlugin.hxx +++ b/src/archive/plugins/Iso9660ArchivePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/plugins/ZzipArchivePlugin.cxx b/src/archive/plugins/ZzipArchivePlugin.cxx index 21cb693d8..4be12b7ed 100644 --- a/src/archive/plugins/ZzipArchivePlugin.cxx +++ b/src/archive/plugins/ZzipArchivePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/archive/plugins/ZzipArchivePlugin.hxx b/src/archive/plugins/ZzipArchivePlugin.hxx index cc92b7c52..f214584bc 100644 --- a/src/archive/plugins/ZzipArchivePlugin.hxx +++ b/src/archive/plugins/ZzipArchivePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/check.h b/src/check.h index efb4556d1..72826a1dc 100644 --- a/src/check.h +++ b/src/check.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/Client.cxx b/src/client/Client.cxx index 01ead4645..d5d153a46 100644 --- a/src/client/Client.cxx +++ b/src/client/Client.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/Client.hxx b/src/client/Client.hxx index c0a940ded..38127b124 100644 --- a/src/client/Client.hxx +++ b/src/client/Client.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include <stddef.h> #include <stdarg.h> -struct sockaddr; +class SocketAddress; class EventLoop; class Path; struct Partition; @@ -51,12 +51,6 @@ public: struct playlist &playlist; struct PlayerControl &player_control; - struct Disposer { - void operator()(Client *client) const { - delete client; - } - }; - unsigned permission; /** the uid of the client process, or -1 if unknown */ @@ -112,7 +106,7 @@ public: void Close(); void SetExpired(); - using FullyBufferedSocket::Write; + bool Write(const void *data, size_t length); /** * returns the uid of the client process, or a negative value @@ -200,11 +194,12 @@ private: virtual void OnTimeout() override; }; -void client_manager_init(void); +void +client_manager_init(); void client_new(EventLoop &loop, Partition &partition, - int fd, const sockaddr *sa, size_t sa_length, int uid); + int fd, SocketAddress address, int uid); /** * Write a C string to the client. diff --git a/src/client/ClientEvent.cxx b/src/client/ClientEvent.cxx index fd9f24b0d..0d330e975 100644 --- a/src/client/ClientEvent.cxx +++ b/src/client/ClientEvent.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,8 +18,7 @@ */ #include "config.h" -#include "ClientInternal.hxx" -#include "util/Error.hxx" +#include "Client.hxx" #include "Log.hxx" void diff --git a/src/client/ClientExpire.cxx b/src/client/ClientExpire.cxx index 5891756b6..70527312d 100644 --- a/src/client/ClientExpire.cxx +++ b/src/client/ClientExpire.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientFile.cxx b/src/client/ClientFile.cxx index 3ea8034d2..37979c1f9 100644 --- a/src/client/ClientFile.cxx +++ b/src/client/ClientFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,10 +21,9 @@ #include "Client.hxx" #include "protocol/Ack.hxx" #include "fs/Path.hxx" -#include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "util/Error.hxx" -#include <sys/stat.h> #include <unistd.h> bool @@ -47,13 +46,11 @@ Client::AllowFile(Path path_fs, Error &error) const return false; } - struct stat st; - if (!StatFile(path_fs, st)) { - error.SetErrno(); + FileInfo fi; + if (!GetFileInfo(path_fs, fi, error)) return false; - } - if (st.st_uid != (uid_t)uid && (st.st_mode & 0444) != 0444) { + if (fi.GetUid() != (uid_t)uid && (fi.GetMode() & 0444) != 0444) { /* client is not owner */ error.Set(ack_domain, ACK_ERROR_PERMISSION, "Access denied"); return false; diff --git a/src/client/ClientGlobal.cxx b/src/client/ClientGlobal.cxx index 8d90721e9..078fd0981 100644 --- a/src/client/ClientGlobal.cxx +++ b/src/client/ClientGlobal.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -31,15 +31,15 @@ size_t client_max_output_buffer_size; void client_manager_init(void) { - client_timeout = config_get_positive(CONF_CONN_TIMEOUT, + client_timeout = config_get_positive(ConfigOption::CONN_TIMEOUT, CLIENT_TIMEOUT_DEFAULT); client_max_command_list_size = - config_get_positive(CONF_MAX_COMMAND_LIST_SIZE, + config_get_positive(ConfigOption::MAX_COMMAND_LIST_SIZE, CLIENT_MAX_COMMAND_LIST_DEFAULT / 1024) * 1024; client_max_output_buffer_size = - config_get_positive(CONF_MAX_OUTPUT_BUFFER_SIZE, + config_get_positive(ConfigOption::MAX_OUTPUT_BUFFER_SIZE, CLIENT_MAX_OUTPUT_BUFFER_SIZE_DEFAULT / 1024) * 1024; } diff --git a/src/client/ClientIdle.cxx b/src/client/ClientIdle.cxx index 0b4fa5751..b43647b6c 100644 --- a/src/client/ClientIdle.cxx +++ b/src/client/ClientIdle.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientInternal.hxx b/src/client/ClientInternal.hxx index a819d64b8..66bdfbb0f 100644 --- a/src/client/ClientInternal.hxx +++ b/src/client/ClientInternal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientList.cxx b/src/client/ClientList.cxx index a1f286928..375e99090 100644 --- a/src/client/ClientList.cxx +++ b/src/client/ClientList.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,8 +20,7 @@ #include "config.h" #include "ClientList.hxx" #include "ClientInternal.hxx" - -#include <algorithm> +#include "util/DeleteDisposer.hxx" #include <assert.h> @@ -36,7 +35,7 @@ ClientList::Remove(Client &client) void ClientList::CloseAll() { - list.clear_and_dispose(Client::Disposer()); + list.clear_and_dispose(DeleteDisposer()); } void diff --git a/src/client/ClientList.hxx b/src/client/ClientList.hxx index 7d20a8737..68292097c 100644 --- a/src/client/ClientList.hxx +++ b/src/client/ClientList.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientMessage.cxx b/src/client/ClientMessage.cxx index be6d2f007..84838219e 100644 --- a/src/client/ClientMessage.cxx +++ b/src/client/ClientMessage.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientMessage.hxx b/src/client/ClientMessage.hxx index bc6a4cd41..13804e715 100644 --- a/src/client/ClientMessage.hxx +++ b/src/client/ClientMessage.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_CLIENT_MESSAGE_H -#define MPD_CLIENT_MESSAGE_H +#ifndef MPD_CLIENT_MESSAGE_HXX +#define MPD_CLIENT_MESSAGE_HXX #include "Compiler.h" diff --git a/src/client/ClientNew.cxx b/src/client/ClientNew.cxx index a080e9ec6..90c81f2c6 100644 --- a/src/client/ClientNew.cxx +++ b/src/client/ClientNew.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,7 +23,8 @@ #include "Partition.hxx" #include "Instance.hxx" #include "system/fd_util.h" -#include "system/Resolver.hxx" +#include "net/SocketAddress.hxx" +#include "net/ToString.hxx" #include "Permission.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -58,15 +59,15 @@ Client::Client(EventLoop &_loop, Partition &_partition, void client_new(EventLoop &loop, Partition &partition, - int fd, const struct sockaddr *sa, size_t sa_length, int uid) + int fd, SocketAddress address, int uid) { static unsigned int next_client_num; - const auto remote = sockaddr_to_string(sa, sa_length); + const auto remote = ToString(address); assert(fd >= 0); #ifdef HAVE_LIBWRAP - if (sa->sa_family != AF_UNIX) { + if (address.GetFamily() != AF_UNIX) { // TODO: shall we obtain the program name from argv[0]? const char *progname = "mpd"; diff --git a/src/client/ClientProcess.cxx b/src/client/ClientProcess.cxx index 0b6953371..1502e0d96 100644 --- a/src/client/ClientProcess.cxx +++ b/src/client/ClientProcess.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,8 +22,7 @@ #include "protocol/Result.hxx" #include "command/AllCommands.hxx" #include "Log.hxx" - -#include <string.h> +#include "util/StringAPI.hxx" #define CLIENT_LIST_MODE_BEGIN "command_list_begin" #define CLIENT_LIST_OK_MODE_BEGIN "command_list_ok_begin" @@ -56,7 +55,7 @@ client_process_line(Client &client, char *line) { CommandResult ret; - if (strcmp(line, "noidle") == 0) { + if (StringIsEqual(line, "noidle")) { if (client.idle_waiting) { /* send empty idle response and leave idle mode */ client.idle_waiting = false; @@ -78,7 +77,7 @@ client_process_line(Client &client, char *line) } if (client.cmd_list.IsActive()) { - if (strcmp(line, CLIENT_LIST_MODE_END) == 0) { + if (StringIsEqual(line, CLIENT_LIST_MODE_END)) { FormatDebug(client_domain, "[%u] process command list", client.num); @@ -113,10 +112,10 @@ client_process_line(Client &client, char *line) ret = CommandResult::OK; } } else { - if (strcmp(line, CLIENT_LIST_MODE_BEGIN) == 0) { + if (StringIsEqual(line, CLIENT_LIST_MODE_BEGIN)) { client.cmd_list.Begin(false); ret = CommandResult::OK; - } else if (strcmp(line, CLIENT_LIST_OK_MODE_BEGIN) == 0) { + } else if (StringIsEqual(line, CLIENT_LIST_OK_MODE_BEGIN)) { client.cmd_list.Begin(true); ret = CommandResult::OK; } else { diff --git a/src/client/ClientRead.cxx b/src/client/ClientRead.cxx index 9cfb1271f..7a1e937ed 100644 --- a/src/client/ClientRead.cxx +++ b/src/client/ClientRead.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientSubscribe.cxx b/src/client/ClientSubscribe.cxx index 8ea2e363b..ebc4848f5 100644 --- a/src/client/ClientSubscribe.cxx +++ b/src/client/ClientSubscribe.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/client/ClientWrite.cxx b/src/client/ClientWrite.cxx index b5d172a8d..f30f2f8b3 100644 --- a/src/client/ClientWrite.cxx +++ b/src/client/ClientWrite.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,35 +18,29 @@ */ #include "config.h" -#include "ClientInternal.hxx" +#include "Client.hxx" #include "util/FormatString.hxx" #include <string.h> -/** - * Write a block of data to the client. - */ -static void -client_write(Client &client, const char *data, size_t length) +bool +Client::Write(const void *data, size_t length) { /* if the client is going to be closed, do nothing */ - if (client.IsExpired() || length == 0) - return; - - client.Write(data, length); + return !IsExpired() && FullyBufferedSocket::Write(data, length); } void client_puts(Client &client, const char *s) { - client_write(client, s, strlen(s)); + client.Write(s, strlen(s)); } void client_vprintf(Client &client, const char *fmt, va_list args) { char *p = FormatNewV(fmt, args); - client_write(client, p, strlen(p)); + client.Write(p, strlen(p)); delete[] p; } diff --git a/src/client/Response.cxx b/src/client/Response.cxx new file mode 100644 index 000000000..d443e66a5 --- /dev/null +++ b/src/client/Response.cxx @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2003-2015 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 "Response.hxx" +#include "Client.hxx" +#include "util/FormatString.hxx" + +#include <string.h> + +bool +Response::Write(const void *data, size_t length) +{ + return client.Write(data, length); +} + +bool +Response::Write(const char *data) +{ + return Write(data, strlen(data)); +} + +bool +Response::FormatV(const char *fmt, va_list args) +{ + char *p = FormatNewV(fmt, args); + bool success = Write(p); + delete[] p; + return success; +} + +bool +Response::Format(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool success = FormatV(fmt, args); + va_end(args); + return success; +} + +void +Response::Error(enum ack code, const char *msg) +{ + FormatError(code, "%s", msg); +} + +void +Response::FormatError(enum ack code, const char *fmt, ...) +{ + Format("ACK [%i@%u] {%s} ", + (int)code, list_index, command); + + va_list args; + va_start(args, fmt); + FormatV(fmt, args); + va_end(args); + + Write("\n"); +} diff --git a/src/client/Response.hxx b/src/client/Response.hxx new file mode 100644 index 000000000..5841e7f61 --- /dev/null +++ b/src/client/Response.hxx @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2003-2015 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_RESPONSE_HXX +#define MPD_RESPONSE_HXX + +#include "check.h" +#include "protocol/Ack.hxx" + +#include <stddef.h> +#include <stdarg.h> + +class Client; + +class Response { + Client &client; + + /** + * This command's index in the command list. Used to generate + * error messages. + */ + const unsigned list_index; + + /** + * This command's name. Used to generate error messages. + */ + const char *command; + +public: + Response(Client &_client, unsigned _list_index) + :client(_client), list_index(_list_index), command("") {} + + Response(const Response &) = delete; + Response &operator=(const Response &) = delete; + + void SetCommand(const char *_command) { + command = _command; + } + + bool Write(const void *data, size_t length); + bool Write(const char *data); + bool FormatV(const char *fmt, va_list args); + bool Format(const char *fmt, ...); + + void Error(enum ack code, const char *msg); + void FormatError(enum ack code, const char *fmt, ...); +}; + +#endif diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx index 6a4b18198..8e8865ff3 100644 --- a/src/command/AllCommands.cxx +++ b/src/command/AllCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "AllCommands.hxx" +#include "Request.hxx" #include "QueueCommands.hxx" #include "TagCommands.hxx" #include "PlayerCommands.hxx" @@ -32,11 +33,14 @@ #include "OtherCommands.hxx" #include "Permission.hxx" #include "tag/TagType.h" -#include "protocol/Result.hxx" #include "Partition.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" +#include "util/Macros.hxx" #include "util/Tokenizer.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" +#include "util/StringAPI.hxx" #ifdef ENABLE_SQLITE #include "StickerCommands.hxx" @@ -60,22 +64,22 @@ struct command { unsigned permission; int min; int max; - CommandResult (*handler)(Client &client, unsigned argc, char **argv); + CommandResult (*handler)(Client &client, Request request, Response &response); }; /* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, unsigned argc, char *argv[]); +handle_commands(Client &client, Request request, Response &response); static CommandResult -handle_not_commands(Client &client, unsigned argc, char *argv[]); +handle_not_commands(Client &client, Request request, Response &response); /** * The command registry. * * This array must be sorted! */ -static const struct command commands[] = { +static constexpr struct command commands[] = { { "add", PERMISSION_ADD, 1, 1, handle_add }, { "addid", PERMISSION_ADD, 1, 2, handle_addid }, { "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid }, @@ -194,62 +198,79 @@ static const struct command commands[] = { { "volume", PERMISSION_CONTROL, 1, 1, handle_volume }, }; -static const unsigned num_commands = sizeof(commands) / sizeof(commands[0]); +static constexpr unsigned num_commands = ARRAY_SIZE(commands); static bool command_available(gcc_unused const Partition &partition, gcc_unused const struct command *cmd) { #ifdef ENABLE_SQLITE - if (strcmp(cmd->cmd, "sticker") == 0) + if (StringIsEqual(cmd->cmd, "sticker")) return sticker_enabled(); #endif #ifdef ENABLE_NEIGHBOR_PLUGINS - if (strcmp(cmd->cmd, "listneighbors") == 0) + if (StringIsEqual(cmd->cmd, "listneighbors")) return neighbor_commands_available(partition.instance); #endif + if (StringIsEqual(cmd->cmd, "save") || + StringIsEqual(cmd->cmd, "rm") || + StringIsEqual(cmd->cmd, "rename") || + StringIsEqual(cmd->cmd, "playlistdelete") || + StringIsEqual(cmd->cmd, "playlistmove") || + StringIsEqual(cmd->cmd, "playlistclear") || + StringIsEqual(cmd->cmd, "playlistadd") || + StringIsEqual(cmd->cmd, "listplaylists")) + return playlist_commands_available(); + return true; } -/* don't be fooled, this is the command handler for "commands" command */ static CommandResult -handle_commands(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +PrintAvailableCommands(Response &r, const Partition &partition, + unsigned permission) { - const unsigned permission = client.GetPermission(); - const struct command *cmd; - for (unsigned i = 0; i < num_commands; ++i) { - cmd = &commands[i]; + const struct command *cmd = &commands[i]; if (cmd->permission == (permission & cmd->permission) && - command_available(client.partition, cmd)) - client_printf(client, "command: %s\n", cmd->cmd); + command_available(partition, cmd)) + r.Format("command: %s\n", cmd->cmd); } return CommandResult::OK; } static CommandResult -handle_not_commands(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +PrintUnavailableCommands(Response &r, unsigned permission) { - const unsigned permission = client.GetPermission(); - const struct command *cmd; - for (unsigned i = 0; i < num_commands; ++i) { - cmd = &commands[i]; + const struct command *cmd = &commands[i]; if (cmd->permission != (permission & cmd->permission)) - client_printf(client, "command: %s\n", cmd->cmd); + r.Format("command: %s\n", cmd->cmd); } return CommandResult::OK; } -void command_init(void) +/* don't be fooled, this is the command handler for "commands" command */ +static CommandResult +handle_commands(Client &client, gcc_unused Request request, Response &r) +{ + return PrintAvailableCommands(r, client.partition, + client.GetPermission()); +} + +static CommandResult +handle_not_commands(Client &client, gcc_unused Request request, Response &r) +{ + return PrintUnavailableCommands(r, client.GetPermission()); +} + +void +command_init() { #ifndef NDEBUG /* ensure that the command list is sorted */ @@ -258,7 +279,8 @@ void command_init(void) #endif } -void command_finish(void) +void +command_finish() { } @@ -266,13 +288,12 @@ static const struct command * command_lookup(const char *name) { unsigned a = 0, b = num_commands, i; - int cmp; /* binary search */ do { i = (a + b) / 2; - cmp = strcmp(name, commands[i].cmd); + const auto cmp = strcmp(name, commands[i].cmd); if (cmp == 0) return &commands[i]; else if (cmp < 0) @@ -285,60 +306,53 @@ command_lookup(const char *name) } static bool -command_check_request(const struct command *cmd, Client &client, - unsigned permission, unsigned argc, char *argv[]) +command_check_request(const struct command *cmd, Response &r, + unsigned permission, Request args) { - const unsigned min = cmd->min + 1; - const unsigned max = cmd->max + 1; - if (cmd->permission != (permission & cmd->permission)) { - command_error(client, ACK_ERROR_PERMISSION, + r.FormatError(ACK_ERROR_PERMISSION, "you don't have permission for \"%s\"", cmd->cmd); return false; } - if (min == 0) + const int min = cmd->min; + const int max = cmd->max; + + if (min < 0) return true; - if (min == max && max != argc) { - command_error(client, ACK_ERROR_ARG, + if (min == max && unsigned(max) != args.size) { + r.FormatError(ACK_ERROR_ARG, "wrong number of arguments for \"%s\"", - argv[0]); + cmd->cmd); return false; - } else if (argc < min) { - command_error(client, ACK_ERROR_ARG, - "too few arguments for \"%s\"", argv[0]); + } else if (args.size < unsigned(min)) { + r.FormatError(ACK_ERROR_ARG, + "too few arguments for \"%s\"", cmd->cmd); return false; - } else if (argc > max && max /* != 0 */ ) { - command_error(client, ACK_ERROR_ARG, - "too many arguments for \"%s\"", argv[0]); + } else if (max >= 0 && args.size > unsigned(max)) { + r.FormatError(ACK_ERROR_ARG, + "too many arguments for \"%s\"", cmd->cmd); return false; } else return true; } static const struct command * -command_checked_lookup(Client &client, unsigned permission, - unsigned argc, char *argv[]) +command_checked_lookup(Response &r, unsigned permission, + const char *cmd_name, Request args) { - const struct command *cmd; - - current_command = ""; - - if (argc == 0) - return nullptr; - - cmd = command_lookup(argv[0]); + const struct command *cmd = command_lookup(cmd_name); if (cmd == nullptr) { - command_error(client, ACK_ERROR_UNKNOWN, - "unknown command \"%s\"", argv[0]); + r.FormatError(ACK_ERROR_UNKNOWN, + "unknown command \"%s\"", cmd_name); return nullptr; } - current_command = cmd->cmd; + r.SetCommand(cmd->cmd); - if (!command_check_request(cmd, client, permission, argc, argv)) + if (!command_check_request(cmd, r, permission, args)) return nullptr; return cmd; @@ -347,68 +361,59 @@ command_checked_lookup(Client &client, unsigned permission, CommandResult command_process(Client &client, unsigned num, char *line) { + Response r(client, num); Error error; - char *argv[COMMAND_ARGV_MAX] = { nullptr }; - const struct command *cmd; - CommandResult ret = CommandResult::ERROR; - - command_list_num = num; /* get the command name (first word on the line) */ + /* we have to set current_command because Response::Error() + expects it to be set */ Tokenizer tokenizer(line); - argv[0] = tokenizer.NextWord(error); - if (argv[0] == nullptr) { - current_command = ""; + + const char *const cmd_name = tokenizer.NextWord(error); + if (cmd_name == nullptr) { if (tokenizer.IsEnd()) - command_error(client, ACK_ERROR_UNKNOWN, - "No command given"); + r.FormatError(ACK_ERROR_UNKNOWN, "No command given"); else - command_error(client, ACK_ERROR_UNKNOWN, - "%s", error.GetMessage()); - - current_command = nullptr; + r.Error(ACK_ERROR_UNKNOWN, error.GetMessage()); /* this client does not speak the MPD protocol; kick the connection */ return CommandResult::FINISH; } - unsigned argc = 1; + char *argv[COMMAND_ARGV_MAX]; + Request args(argv, 0); /* now parse the arguments (quoted or unquoted) */ - while (argc < COMMAND_ARGV_MAX && - (argv[argc] = - tokenizer.NextParam(error)) != nullptr) - ++argc; - - /* some error checks; we have to set current_command because - command_error() expects it to be set */ + while (true) { + if (args.size == COMMAND_ARGV_MAX) { + r.Error(ACK_ERROR_ARG, "Too many arguments"); + return CommandResult::ERROR; + } - current_command = argv[0]; + char *a = tokenizer.NextParam(error); + if (a == nullptr) { + if (tokenizer.IsEnd()) + break; - if (argc >= COMMAND_ARGV_MAX) { - command_error(client, ACK_ERROR_ARG, "Too many arguments"); - current_command = nullptr; - return CommandResult::ERROR; - } + r.Error(ACK_ERROR_UNKNOWN, error.GetMessage()); + return CommandResult::ERROR; + } - if (!tokenizer.IsEnd()) { - command_error(client, ACK_ERROR_ARG, "%s", error.GetMessage()); - current_command = nullptr; - return CommandResult::ERROR; + argv[args.size++] = a; } /* look up and invoke the command handler */ - cmd = command_checked_lookup(client, client.GetPermission(), - argc, argv); - if (cmd) - ret = cmd->handler(client, argc, argv); + const struct command *cmd = + command_checked_lookup(r, client.GetPermission(), + cmd_name, args); - current_command = nullptr; - command_list_num = 0; + CommandResult ret = cmd + ? cmd->handler(client, args, r) + : CommandResult::ERROR; return ret; } diff --git a/src/command/AllCommands.hxx b/src/command/AllCommands.hxx index b7834a8de..e3405b034 100644 --- a/src/command/AllCommands.hxx +++ b/src/command/AllCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,9 +24,11 @@ class Client; -void command_init(void); +void +command_init(); -void command_finish(void); +void +command_finish(); CommandResult command_process(Client &client, unsigned num, char *line); diff --git a/src/command/CommandError.cxx b/src/command/CommandError.cxx index 89085fc68..9f06431b4 100644 --- a/src/command/CommandError.cxx +++ b/src/command/CommandError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,8 @@ #include "config.h" #include "CommandError.hxx" #include "db/DatabaseError.hxx" -#include "protocol/Result.hxx" +#include "LocateUri.hxx" +#include "client/Response.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -29,57 +30,55 @@ #include <errno.h> CommandResult -print_playlist_result(Client &client, PlaylistResult result) +print_playlist_result(Response &r, PlaylistResult result) { switch (result) { case PlaylistResult::SUCCESS: return CommandResult::OK; case PlaylistResult::ERRNO: - command_error(client, ACK_ERROR_SYSTEM, "%s", - strerror(errno)); + r.Error(ACK_ERROR_SYSTEM, strerror(errno)); return CommandResult::ERROR; case PlaylistResult::DENIED: - command_error(client, ACK_ERROR_PERMISSION, "Access denied"); + r.Error(ACK_ERROR_PERMISSION, "Access denied"); return CommandResult::ERROR; case PlaylistResult::NO_SUCH_SONG: - command_error(client, ACK_ERROR_NO_EXIST, "No such song"); + r.Error(ACK_ERROR_NO_EXIST, "No such song"); return CommandResult::ERROR; case PlaylistResult::NO_SUCH_LIST: - command_error(client, ACK_ERROR_NO_EXIST, "No such playlist"); + r.Error(ACK_ERROR_NO_EXIST, "No such playlist"); return CommandResult::ERROR; case PlaylistResult::LIST_EXISTS: - command_error(client, ACK_ERROR_EXIST, - "Playlist already exists"); + r.Error(ACK_ERROR_EXIST, "Playlist already exists"); return CommandResult::ERROR; case PlaylistResult::BAD_NAME: - command_error(client, ACK_ERROR_ARG, - "playlist name is invalid: " - "playlist names may not contain slashes," - " newlines or carriage returns"); + r.Error(ACK_ERROR_ARG, + "playlist name is invalid: " + "playlist names may not contain slashes," + " newlines or carriage returns"); return CommandResult::ERROR; case PlaylistResult::BAD_RANGE: - command_error(client, ACK_ERROR_ARG, "Bad song index"); + r.Error(ACK_ERROR_ARG, "Bad song index"); return CommandResult::ERROR; case PlaylistResult::NOT_PLAYING: - command_error(client, ACK_ERROR_PLAYER_SYNC, "Not playing"); + r.Error(ACK_ERROR_PLAYER_SYNC, "Not playing"); return CommandResult::ERROR; case PlaylistResult::TOO_LARGE: - command_error(client, ACK_ERROR_PLAYLIST_MAX, - "playlist is at the max size"); + r.Error(ACK_ERROR_PLAYLIST_MAX, + "playlist is at the max size"); return CommandResult::ERROR; case PlaylistResult::DISABLED: - command_error(client, ACK_ERROR_UNKNOWN, - "stored playlist support is disabled"); + r.Error(ACK_ERROR_UNKNOWN, + "stored playlist support is disabled"); return CommandResult::ERROR; } @@ -88,42 +87,42 @@ print_playlist_result(Client &client, PlaylistResult result) } CommandResult -print_error(Client &client, const Error &error) +print_error(Response &r, const Error &error) { assert(error.IsDefined()); LogError(error); if (error.IsDomain(playlist_domain)) { - return print_playlist_result(client, + return print_playlist_result(r, PlaylistResult(error.GetCode())); } else if (error.IsDomain(ack_domain)) { - command_error(client, (ack)error.GetCode(), - "%s", error.GetMessage()); + r.Error((ack)error.GetCode(), error.GetMessage()); return CommandResult::ERROR; #ifdef ENABLE_DATABASE } else if (error.IsDomain(db_domain)) { switch ((enum db_error)error.GetCode()) { case DB_DISABLED: - command_error(client, ACK_ERROR_NO_EXIST, "%s", - error.GetMessage()); + r.Error(ACK_ERROR_NO_EXIST, error.GetMessage()); return CommandResult::ERROR; case DB_NOT_FOUND: - command_error(client, ACK_ERROR_NO_EXIST, "Not found"); + r.Error(ACK_ERROR_NO_EXIST, "Not found"); return CommandResult::ERROR; case DB_CONFLICT: - command_error(client, ACK_ERROR_ARG, "Conflict"); + r.Error(ACK_ERROR_ARG, "Conflict"); return CommandResult::ERROR; } #endif + } else if (error.IsDomain(locate_uri_domain)) { + r.Error(ACK_ERROR_ARG, error.GetMessage()); + return CommandResult::ERROR; } else if (error.IsDomain(errno_domain)) { - command_error(client, ACK_ERROR_SYSTEM, "%s", - strerror(error.GetCode())); + r.Error(ACK_ERROR_SYSTEM, strerror(error.GetCode())); return CommandResult::ERROR; } - command_error(client, ACK_ERROR_UNKNOWN, "error"); + r.Error(ACK_ERROR_UNKNOWN, "error"); return CommandResult::ERROR; } diff --git a/src/command/CommandError.hxx b/src/command/CommandError.hxx index b48baa5bf..e33386078 100644 --- a/src/command/CommandError.hxx +++ b/src/command/CommandError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,16 +23,16 @@ #include "CommandResult.hxx" #include "PlaylistError.hxx" -class Client; +class Response; class Error; CommandResult -print_playlist_result(Client &client, PlaylistResult result); +print_playlist_result(Response &r, PlaylistResult result); /** * Send the #Error to the client. */ CommandResult -print_error(Client &client, const Error &error); +print_error(Response &r, const Error &error); #endif diff --git a/src/command/CommandListBuilder.cxx b/src/command/CommandListBuilder.cxx index 477c246ff..4abb3ad16 100644 --- a/src/command/CommandListBuilder.cxx +++ b/src/command/CommandListBuilder.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,6 @@ void CommandListBuilder::Reset() { list.clear(); - size = 0; mode = Mode::DISABLED; } diff --git a/src/command/CommandListBuilder.hxx b/src/command/CommandListBuilder.hxx index 0747c4697..9908121d6 100644 --- a/src/command/CommandListBuilder.hxx +++ b/src/command/CommandListBuilder.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -58,7 +58,7 @@ class CommandListBuilder { public: CommandListBuilder() - :mode(Mode::DISABLED), size(0) {} + :mode(Mode::DISABLED) {} /** * Is a command list currently being built? @@ -89,6 +89,7 @@ public: assert(mode == Mode::DISABLED); mode = (Mode)ok; + size = 0; } /** diff --git a/src/command/CommandResult.hxx b/src/command/CommandResult.hxx index a2e968fb6..13641ec39 100644 --- a/src/command/CommandResult.hxx +++ b/src/command/CommandResult.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/command/DatabaseCommands.cxx b/src/command/DatabaseCommands.cxx index a3ea8d0ae..bfcf3aa54 100644 --- a/src/command/DatabaseCommands.cxx +++ b/src/command/DatabaseCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "DatabaseCommands.hxx" +#include "Request.hxx" #include "db/DatabaseGlue.hxx" #include "db/DatabaseQueue.hxx" #include "db/DatabasePlaylist.hxx" @@ -27,83 +28,89 @@ #include "db/Selection.hxx" #include "CommandError.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "tag/Tag.hxx" #include "util/ConstBuffer.hxx" #include "util/Error.hxx" +#include "util/StringAPI.hxx" #include "SongFilter.hxx" -#include "protocol/Result.hxx" #include "BulkEdit.hxx" #include <string.h> CommandResult -handle_listfiles_db(Client &client, const char *uri) +handle_listfiles_db(Client &client, Response &r, const char *uri) { const DatabaseSelection selection(uri, false); Error error; - if (!db_selection_print(client, selection, false, true, error)) - return print_error(client, error); + if (!db_selection_print(r, client.partition, + selection, false, true, error)) + return print_error(r, error); return CommandResult::OK; } CommandResult -handle_lsinfo2(Client &client, unsigned argc, char *argv[]) +handle_lsinfo2(Client &client, const char *uri, Response &r) { - const char *const uri = argc == 2 - ? argv[1] - /* default is root directory */ - : ""; - const DatabaseSelection selection(uri, false); Error error; - if (!db_selection_print(client, selection, true, false, error)) - return print_error(client, error); + if (!db_selection_print(r, client.partition, + selection, true, false, error)) + return print_error(r, error); return CommandResult::OK; } static CommandResult -handle_match(Client &client, unsigned argc, char *argv[], bool fold_case) +handle_match(Client &client, Request args, Response &r, bool fold_case) { - ConstBuffer<const char *> args(argv + 1, argc - 1); + RangeArg window; + if (args.size >= 2 && StringIsEqual(args[args.size - 2], "window")) { + if (!args.Parse(args.size - 1, window, r)) + return CommandResult::ERROR; + + args.pop_back(); + args.pop_back(); + } else + window.SetAll(); SongFilter filter; if (!filter.Parse(args, fold_case)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } const DatabaseSelection selection("", true, &filter); Error error; - return db_selection_print(client, selection, true, false, error) + return db_selection_print(r, client.partition, + selection, true, false, + window.start, window.end, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_find(Client &client, unsigned argc, char *argv[]) +handle_find(Client &client, Request args, Response &r) { - return handle_match(client, argc, argv, false); + return handle_match(client, args, r, false); } CommandResult -handle_search(Client &client, unsigned argc, char *argv[]) +handle_search(Client &client, Request args, Response &r) { - return handle_match(client, argc, argv, true); + return handle_match(client, args, r, true); } static CommandResult -handle_match_add(Client &client, unsigned argc, char *argv[], bool fold_case) +handle_match_add(Client &client, Request args, Response &r, bool fold_case) { - ConstBuffer<const char *> args(argv + 1, argc - 1); - SongFilter filter; if (!filter.Parse(args, fold_case)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } @@ -113,55 +120,52 @@ handle_match_add(Client &client, unsigned argc, char *argv[], bool fold_case) Error error; return AddFromDatabase(client.partition, selection, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_findadd(Client &client, unsigned argc, char *argv[]) +handle_findadd(Client &client, Request args, Response &r) { - return handle_match_add(client, argc, argv, false); + return handle_match_add(client, args, r, false); } CommandResult -handle_searchadd(Client &client, unsigned argc, char *argv[]) +handle_searchadd(Client &client, Request args, Response &r) { - return handle_match_add(client, argc, argv, true); + return handle_match_add(client, args, r, true); } CommandResult -handle_searchaddpl(Client &client, unsigned argc, char *argv[]) +handle_searchaddpl(Client &client, Request args, Response &r) { - ConstBuffer<const char *> args(argv + 1, argc - 1); const char *playlist = args.shift(); SongFilter filter; if (!filter.Parse(args, true)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } Error error; const Database *db = client.GetDatabase(error); if (db == nullptr) - return print_error(client, error); + return print_error(r, error); return search_add_to_playlist(*db, *client.GetStorage(), "", playlist, &filter, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_count(Client &client, unsigned argc, char *argv[]) +handle_count(Client &client, Request args, Response &r) { - ConstBuffer<const char *> args(argv + 1, argc - 1); - TagType group = TAG_NUM_OF_ITEM_TYPES; - if (args.size >= 2 && strcmp(args[args.size - 2], "group") == 0) { + if (args.size >= 2 && StringIsEqual(args[args.size - 2], "group")) { const char *s = args[args.size - 1]; group = tag_name_parse_i(s); if (group == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", s); return CommandResult::ERROR; } @@ -172,52 +176,50 @@ handle_count(Client &client, unsigned argc, char *argv[]) SongFilter filter; if (!args.IsEmpty() && !filter.Parse(args, false)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } Error error; - return PrintSongCount(client, "", &filter, group, error) + return PrintSongCount(r, client.partition, "", &filter, group, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_listall(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_listall(Client &client, Request args, Response &r) { - const char *directory = ""; - - if (argc == 2) - directory = argv[1]; + /* default is root directory */ + const auto uri = args.GetOptional(0, ""); Error error; - return db_selection_print(client, DatabaseSelection(directory, true), + return db_selection_print(r, client.partition, + DatabaseSelection(uri, true), false, false, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_list(Client &client, unsigned argc, char *argv[]) +handle_list(Client &client, Request args, Response &r) { - ConstBuffer<const char *> args(argv + 1, argc - 1); const char *tag_name = args.shift(); unsigned tagType = locate_parse_type(tag_name); if (tagType >= TAG_NUM_OF_ITEM_TYPES && tagType != LOCATE_TAG_FILE_TYPE) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name); return CommandResult::ERROR; } SongFilter *filter = nullptr; - uint32_t group_mask = 0; + tag_mask_t group_mask = 0; if (args.size == 1) { /* for compatibility with < 0.12.0 */ if (tagType != TAG_ALBUM) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "should be \"%s\" for 3 arguments", tag_item_names[TAG_ALBUM]); return CommandResult::ERROR; @@ -227,16 +229,16 @@ handle_list(Client &client, unsigned argc, char *argv[]) } while (args.size >= 2 && - strcmp(args[args.size - 2], "group") == 0) { + StringIsEqual(args[args.size - 2], "group")) { const char *s = args[args.size - 1]; TagType gt = tag_name_parse_i(s); if (gt == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", s); return CommandResult::ERROR; } - group_mask |= 1u << unsigned(gt); + group_mask |= tag_mask_t(1) << unsigned(gt); args.pop_back(); args.pop_back(); @@ -246,24 +248,24 @@ handle_list(Client &client, unsigned argc, char *argv[]) filter = new SongFilter(); if (!filter->Parse(args, false)) { delete filter; - command_error(client, ACK_ERROR_ARG, - "not able to parse args"); + r.Error(ACK_ERROR_ARG, "not able to parse args"); return CommandResult::ERROR; } } if (tagType < TAG_NUM_OF_ITEM_TYPES && - group_mask & (1u << tagType)) { + group_mask & (tag_mask_t(1) << tagType)) { delete filter; - command_error(client, ACK_ERROR_ARG, "Conflicting group"); + r.Error(ACK_ERROR_ARG, "Conflicting group"); return CommandResult::ERROR; } Error error; CommandResult ret = - PrintUniqueTags(client, tagType, group_mask, filter, error) + PrintUniqueTags(r, client.partition, + tagType, group_mask, filter, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); delete filter; @@ -271,16 +273,15 @@ handle_list(Client &client, unsigned argc, char *argv[]) } CommandResult -handle_listallinfo(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_listallinfo(Client &client, Request args, Response &r) { - const char *directory = ""; - - if (argc == 2) - directory = argv[1]; + /* default is root directory */ + const auto uri = args.GetOptional(0, ""); Error error; - return db_selection_print(client, DatabaseSelection(directory, true), + return db_selection_print(r, client.partition, + DatabaseSelection(uri, true), true, false, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } diff --git a/src/command/DatabaseCommands.hxx b/src/command/DatabaseCommands.hxx index 7abf89e0c..660e147db 100644 --- a/src/command/DatabaseCommands.hxx +++ b/src/command/DatabaseCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,38 +23,40 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_listfiles_db(Client &client, const char *uri); +handle_listfiles_db(Client &client, Response &r, const char *uri); CommandResult -handle_lsinfo2(Client &client, unsigned argc, char *argv[]); +handle_lsinfo2(Client &client, const char *uri, Response &response); CommandResult -handle_find(Client &client, unsigned argc, char *argv[]); +handle_find(Client &client, Request request, Response &response); CommandResult -handle_findadd(Client &client, unsigned argc, char *argv[]); +handle_findadd(Client &client, Request request, Response &response); CommandResult -handle_search(Client &client, unsigned argc, char *argv[]); +handle_search(Client &client, Request request, Response &response); CommandResult -handle_searchadd(Client &client, unsigned argc, char *argv[]); +handle_searchadd(Client &client, Request request, Response &response); CommandResult -handle_searchaddpl(Client &client, unsigned argc, char *argv[]); +handle_searchaddpl(Client &client, Request request, Response &response); CommandResult -handle_count(Client &client, unsigned argc, char *argv[]); +handle_count(Client &client, Request request, Response &response); CommandResult -handle_listall(Client &client, unsigned argc, char *argv[]); +handle_listall(Client &client, Request request, Response &response); CommandResult -handle_list(Client &client, unsigned argc, char *argv[]); +handle_list(Client &client, Request request, Response &response); CommandResult -handle_listallinfo(Client &client, unsigned argc, char *argv[]); +handle_listallinfo(Client &client, Request request, Response &response); #endif diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index 1b6a11cf5..486c00d89 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,10 +21,12 @@ #include "config.h" #include "FileCommands.hxx" +#include "Request.hxx" #include "CommandError.hxx" #include "protocol/Ack.hxx" -#include "protocol/Result.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" +#include "util/ConstBuffer.hxx" #include "util/CharUtil.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" @@ -35,10 +37,10 @@ #include "TagFile.hxx" #include "storage/StorageInterface.hxx" #include "fs/AllocatedPath.hxx" -#include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "fs/DirectoryReader.hxx" +#include "LocateUri.hxx" #include "TimePrint.hxx" -#include "ls.hxx" #include <assert.h> #include <sys/stat.h> @@ -46,7 +48,7 @@ gcc_pure static bool -SkipNameFS(const char *name_fs) +SkipNameFS(PathTraitsFS::const_pointer name_fs) { return name_fs[0] == '.' && (name_fs[1] == 0 || @@ -55,9 +57,9 @@ SkipNameFS(const char *name_fs) gcc_pure static bool -skip_path(const char *name_fs) +skip_path(Path name_fs) { - return strchr(name_fs, '\n') != nullptr; + return name_fs.HasNewline(); } #if defined(WIN32) && GCC_CHECK_VERSION(4,6) @@ -68,28 +70,19 @@ skip_path(const char *name_fs) #endif CommandResult -handle_listfiles_local(Client &client, const char *path_utf8) +handle_listfiles_local(Response &r, + const char *path_utf8, Path path_fs) { - const auto path_fs = AllocatedPath::FromUTF8(path_utf8); - if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); - return CommandResult::ERROR; - } - - Error error; - if (!client.AllowFile(path_fs, error)) - return print_error(client, error); - DirectoryReader reader(path_fs); if (reader.HasFailed()) { + Error error; error.FormatErrno("Failed to open '%s'", path_utf8); - return print_error(client, error); + return print_error(r, error); } while (reader.ReadEntry()) { const Path name_fs = reader.GetEntry(); - if (SkipNameFS(name_fs.c_str()) || skip_path(name_fs.c_str())) + if (SkipNameFS(name_fs.c_str()) || skip_path(name_fs)) continue; std::string name_utf8 = name_fs.ToUTF8(); @@ -98,20 +91,21 @@ handle_listfiles_local(Client &client, const char *path_utf8) const AllocatedPath full_fs = AllocatedPath::Build(path_fs, name_fs); - struct stat st; - if (!StatFile(full_fs, st, false)) + FileInfo fi; + if (!GetFileInfo(full_fs, fi, false)) continue; - if (S_ISREG(st.st_mode)) { - client_printf(client, "file: %s\n" - "size: %" PRIu64 "\n", - name_utf8.c_str(), - uint64_t(st.st_size)); - } else if (S_ISDIR(st.st_mode)) - client_printf(client, "directory: %s\n", - name_utf8.c_str()); + if (fi.IsRegular()) + r.Format("file: %s\n" + "size: %" PRIu64 "\n", + name_utf8.c_str(), + fi.GetSize()); + else if (fi.IsDirectory()) + r.Format("directory: %s\n", name_utf8.c_str()); + else + continue; - time_print(client, "Last-Modified", st.st_mtime); + time_print(r, "Last-Modified", fi.GetModificationTime()); } return CommandResult::OK; @@ -154,10 +148,10 @@ IsValidValue(const char *p) static void print_pair(const char *key, const char *value, void *ctx) { - Client &client = *(Client *)ctx; + auto &r = *(Response *)ctx; if (IsValidName(key) && IsValidValue(value)) - client_printf(client, "%s: %s\n", key, value); + r.Format("%s: %s\n", key, value); } static constexpr tag_handler print_comment_handler = { @@ -167,93 +161,90 @@ static constexpr tag_handler print_comment_handler = { }; static CommandResult -read_stream_comments(Client &client, const char *uri) +read_stream_comments(Response &r, const char *uri) { - if (!uri_supported_scheme(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported URI scheme"); + if (!tag_stream_scan(uri, print_comment_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "Failed to load file"); return CommandResult::ERROR; } - if (!tag_stream_scan(uri, print_comment_handler, &client)) { - command_error(client, ACK_ERROR_NO_EXIST, - "Failed to load file"); + return CommandResult::OK; + +} + +static CommandResult +read_file_comments(Response &r, const Path path_fs) +{ + if (!tag_file_scan(path_fs, print_comment_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "Failed to load file"); return CommandResult::ERROR; } + tag_ape_scan2(path_fs, &print_comment_handler, &r); + tag_id3_scan(path_fs, &print_comment_handler, &r); + return CommandResult::OK; } static CommandResult -read_file_comments(Client &client, const Path path_fs) +read_db_comments(Client &client, Response &r, const char *uri) { - if (!tag_file_scan(path_fs, print_comment_handler, &client)) { - command_error(client, ACK_ERROR_NO_EXIST, - "Failed to load file"); +#ifdef ENABLE_DATABASE + const Storage *storage = client.GetStorage(); + if (storage == nullptr) { +#else + (void)client; + (void)uri; +#endif + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; +#ifdef ENABLE_DATABASE } - tag_ape_scan2(path_fs, &print_comment_handler, &client); - tag_id3_scan(path_fs, &print_comment_handler, &client); + { + AllocatedPath path_fs = storage->MapFS(uri); + if (!path_fs.IsNull()) + return read_file_comments(r, path_fs); + } - return CommandResult::OK; + { + const std::string uri2 = storage->MapUTF8(uri); + if (uri_has_scheme(uri2.c_str())) + return read_stream_comments(r, uri2.c_str()); + } + r.Error(ACK_ERROR_NO_EXIST, "No such file"); + return CommandResult::ERROR; +#endif } CommandResult -handle_read_comments(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_read_comments(Client &client, Request args, Response &r) { - assert(argc == 2); + assert(args.size == 1); - const char *const uri = argv[1]; + const char *const uri = args.front(); - if (memcmp(uri, "file:///", 8) == 0) { - /* read comments from arbitrary local file */ - const char *path_utf8 = uri + 7; - AllocatedPath path_fs = AllocatedPath::FromUTF8(path_utf8); - if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); - return CommandResult::ERROR; - } - - Error error; - if (!client.AllowFile(path_fs, error)) - return print_error(client, error); - - return read_file_comments(client, path_fs); - } else if (uri_has_scheme(uri)) { - return read_stream_comments(client, uri); - } else if (!PathTraitsUTF8::IsAbsolute(uri)) { -#ifdef ENABLE_DATABASE - const Storage *storage = client.GetStorage(); - if (storage == nullptr) { -#endif - command_error(client, ACK_ERROR_NO_EXIST, - "No database"); - return CommandResult::ERROR; + Error error; + const auto located_uri = LocateUri(uri, &client, #ifdef ENABLE_DATABASE - } - - { - AllocatedPath path_fs = storage->MapFS(uri); - if (!path_fs.IsNull()) - return read_file_comments(client, path_fs); - } - - { - const std::string uri2 = storage->MapUTF8(uri); - if (uri_has_scheme(uri2.c_str())) - return read_stream_comments(client, - uri2.c_str()); - } - - command_error(client, ACK_ERROR_NO_EXIST, "No such file"); - return CommandResult::ERROR; + nullptr, #endif - } else { - command_error(client, ACK_ERROR_NO_EXIST, "No such file"); - return CommandResult::ERROR; + error); + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); + + case LocatedUri::Type::ABSOLUTE: + return read_stream_comments(r, located_uri.canonical_uri); + + case LocatedUri::Type::RELATIVE: + return read_db_comments(client, r, located_uri.canonical_uri); + + case LocatedUri::Type::PATH: + return read_file_comments(r, located_uri.path); } + + gcc_unreachable(); } diff --git a/src/command/FileCommands.hxx b/src/command/FileCommands.hxx index 62835a82c..9c6631df5 100644 --- a/src/command/FileCommands.hxx +++ b/src/command/FileCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,11 +23,15 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; +class Path; CommandResult -handle_listfiles_local(Client &client, const char *path_utf8); +handle_listfiles_local(Response &response, + const char *path_utf8, Path path_fs); CommandResult -handle_read_comments(Client &client, unsigned argc, char *argv[]); +handle_read_comments(Client &client, Request request, Response &response); #endif diff --git a/src/command/MessageCommands.cxx b/src/command/MessageCommands.cxx index a86bdf30c..28f97a52f 100644 --- a/src/command/MessageCommands.cxx +++ b/src/command/MessageCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,11 +19,13 @@ #include "config.h" #include "MessageCommands.hxx" +#include "Request.hxx" #include "client/Client.hxx" #include "client/ClientList.hxx" +#include "client/Response.hxx" #include "Instance.hxx" #include "Partition.hxx" -#include "protocol/Result.hxx" +#include "util/ConstBuffer.hxx" #include <set> #include <string> @@ -31,27 +33,25 @@ #include <assert.h> CommandResult -handle_subscribe(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_subscribe(Client &client, Request args, Response &r) { - assert(argc == 2); + assert(args.size == 1); + const char *const channel_name = args[0]; - switch (client.Subscribe(argv[1])) { + switch (client.Subscribe(channel_name)) { case Client::SubscribeResult::OK: return CommandResult::OK; case Client::SubscribeResult::INVALID: - command_error(client, ACK_ERROR_ARG, - "invalid channel name"); + r.Error(ACK_ERROR_ARG, "invalid channel name"); return CommandResult::ERROR; case Client::SubscribeResult::ALREADY: - command_error(client, ACK_ERROR_EXIST, - "already subscribed to this channel"); + r.Error(ACK_ERROR_EXIST, "already subscribed to this channel"); return CommandResult::ERROR; case Client::SubscribeResult::FULL: - command_error(client, ACK_ERROR_EXIST, - "subscription list is full"); + r.Error(ACK_ERROR_EXIST, "subscription list is full"); return CommandResult::ERROR; } @@ -61,24 +61,23 @@ handle_subscribe(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_unsubscribe(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_unsubscribe(Client &client, Request args, Response &r) { - assert(argc == 2); + assert(args.size == 1); + const char *const channel_name = args[0]; - if (client.Unsubscribe(argv[1])) + if (client.Unsubscribe(channel_name)) return CommandResult::OK; else { - command_error(client, ACK_ERROR_NO_EXIST, - "not subscribed to this channel"); + r.Error(ACK_ERROR_NO_EXIST, "not subscribed to this channel"); return CommandResult::ERROR; } } CommandResult -handle_channels(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_channels(Client &client, gcc_unused Request args, Response &r) { - assert(argc == 1); + assert(args.IsEmpty()); std::set<std::string> channels; for (const auto &c : *client.partition.instance.client_list) @@ -86,22 +85,22 @@ handle_channels(Client &client, c.subscriptions.end()); for (const auto &channel : channels) - client_printf(client, "channel: %s\n", channel.c_str()); + r.Format("channel: %s\n", channel.c_str()); return CommandResult::OK; } CommandResult handle_read_messages(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) + gcc_unused Request args, Response &r) { - assert(argc == 1); + assert(args.IsEmpty()); while (!client.messages.empty()) { const ClientMessage &msg = client.messages.front(); - client_printf(client, "channel: %s\nmessage: %s\n", - msg.GetChannel(), msg.GetMessage()); + r.Format("channel: %s\nmessage: %s\n", + msg.GetChannel(), msg.GetMessage()); client.messages.pop_front(); } @@ -109,19 +108,20 @@ handle_read_messages(Client &client, } CommandResult -handle_send_message(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_send_message(Client &client, Request args, Response &r) { - assert(argc == 3); + assert(args.size == 2); - if (!client_message_valid_channel_name(argv[1])) { - command_error(client, ACK_ERROR_ARG, - "invalid channel name"); + const char *const channel_name = args[0]; + const char *const message_text = args[1]; + + if (!client_message_valid_channel_name(channel_name)) { + r.Error(ACK_ERROR_ARG, "invalid channel name"); return CommandResult::ERROR; } bool sent = false; - const ClientMessage msg(argv[1], argv[2]); + const ClientMessage msg(channel_name, message_text); for (auto &c : *client.partition.instance.client_list) if (c.PushMessage(msg)) sent = true; @@ -129,8 +129,8 @@ handle_send_message(Client &client, if (sent) return CommandResult::OK; else { - command_error(client, ACK_ERROR_NO_EXIST, - "nobody is subscribed to this channel"); + r.Error(ACK_ERROR_NO_EXIST, + "nobody is subscribed to this channel"); return CommandResult::ERROR; } } diff --git a/src/command/MessageCommands.hxx b/src/command/MessageCommands.hxx index ac8afe2fb..986c7cf6a 100644 --- a/src/command/MessageCommands.hxx +++ b/src/command/MessageCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,20 +23,22 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_subscribe(Client &client, unsigned argc, char *argv[]); +handle_subscribe(Client &client, Request request, Response &response); CommandResult -handle_unsubscribe(Client &client, unsigned argc, char *argv[]); +handle_unsubscribe(Client &client, Request request, Response &response); CommandResult -handle_channels(Client &client, unsigned argc, char *argv[]); +handle_channels(Client &client, Request request, Response &response); CommandResult -handle_read_messages(Client &client, unsigned argc, char *argv[]); +handle_read_messages(Client &client, Request request, Response &response); CommandResult -handle_send_message(Client &client, unsigned argc, char *argv[]); +handle_send_message(Client &client, Request request, Response &response); #endif diff --git a/src/command/NeighborCommands.cxx b/src/command/NeighborCommands.cxx index 22e8adf9e..254f7a346 100644 --- a/src/command/NeighborCommands.cxx +++ b/src/command/NeighborCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,12 +19,14 @@ #include "config.h" #include "NeighborCommands.hxx" +#include "Request.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Instance.hxx" #include "Partition.hxx" -#include "protocol/Result.hxx" #include "neighbor/Glue.hxx" #include "neighbor/Info.hxx" +#include "util/ConstBuffer.hxx" #include <set> #include <string> @@ -38,22 +40,19 @@ neighbor_commands_available(const Instance &instance) } CommandResult -handle_listneighbors(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_listneighbors(Client &client, gcc_unused Request args, Response &r) { const NeighborGlue *const neighbors = client.partition.instance.neighbors; if (neighbors == nullptr) { - command_error(client, ACK_ERROR_UNKNOWN, - "No neighbor plugin configured"); + r.Error(ACK_ERROR_UNKNOWN, "No neighbor plugin configured"); return CommandResult::ERROR; } for (const auto &i : neighbors->GetList()) - client_printf(client, - "neighbor: %s\n" - "name: %s\n", - i.uri.c_str(), - i.display_name.c_str()); + r.Format("neighbor: %s\n" + "name: %s\n", + i.uri.c_str(), + i.display_name.c_str()); return CommandResult::OK; } diff --git a/src/command/NeighborCommands.hxx b/src/command/NeighborCommands.hxx index 7fb309aeb..efe05c5ae 100644 --- a/src/command/NeighborCommands.hxx +++ b/src/command/NeighborCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,12 +25,14 @@ struct Instance; class Client; +class Request; +class Response; gcc_pure bool neighbor_commands_available(const Instance &instance); CommandResult -handle_listneighbors(Client &client, unsigned argc, char *argv[]); +handle_listneighbors(Client &client, Request request, Response &response); #endif diff --git a/src/command/OtherCommands.cxx b/src/command/OtherCommands.cxx index a924f77b5..b4a23fe4b 100644 --- a/src/command/OtherCommands.cxx +++ b/src/command/OtherCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,11 +19,13 @@ #include "config.h" #include "OtherCommands.hxx" +#include "Request.hxx" #include "FileCommands.hxx" #include "StorageCommands.hxx" #include "CommandError.hxx" #include "db/Uri.hxx" #include "storage/StorageInterface.hxx" +#include "LocateUri.hxx" #include "DetachedSong.hxx" #include "SongPrint.hxx" #include "TagPrint.hxx" @@ -31,18 +33,19 @@ #include "tag/TagHandler.hxx" #include "TimePrint.hxx" #include "decoder/DecoderPrint.hxx" -#include "protocol/ArgParser.hxx" -#include "protocol/Result.hxx" #include "ls.hxx" #include "mixer/Volume.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" +#include "util/StringAPI.hxx" #include "fs/AllocatedPath.hxx" #include "Stats.hxx" #include "Permission.hxx" #include "PlaylistFile.hxx" #include "db/PlaylistVector.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "Idle.hxx" @@ -57,52 +60,51 @@ #include <string.h> static void -print_spl_list(Client &client, const PlaylistVector &list) +print_spl_list(Response &r, const PlaylistVector &list) { for (const auto &i : list) { - client_printf(client, "playlist: %s\n", i.name.c_str()); + r.Format("playlist: %s\n", i.name.c_str()); if (i.mtime > 0) - time_print(client, "Last-Modified", i.mtime); + time_print(r, "Last-Modified", i.mtime); } } CommandResult -handle_urlhandlers(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_urlhandlers(Client &client, gcc_unused Request args, Response &r) { if (client.IsLocal()) - client_puts(client, "handler: file://\n"); - print_supported_uri_schemes(client); + r.Format("handler: file://\n"); + print_supported_uri_schemes(r); return CommandResult::OK; } CommandResult -handle_decoders(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_decoders(gcc_unused Client &client, gcc_unused Request args, + Response &r) { - decoder_list_print(client); + decoder_list_print(r); return CommandResult::OK; } CommandResult -handle_tagtypes(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_tagtypes(gcc_unused Client &client, gcc_unused Request request, + Response &r) { - tag_print_types(client); + tag_print_types(r); return CommandResult::OK; } CommandResult -handle_kill(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_kill(gcc_unused Client &client, gcc_unused Request request, + gcc_unused Response &r) { return CommandResult::KILL; } CommandResult -handle_close(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_close(gcc_unused Client &client, gcc_unused Request args, + gcc_unused Response &r) { return CommandResult::FINISH; } @@ -110,44 +112,60 @@ handle_close(gcc_unused Client &client, static void print_tag(TagType type, const char *value, void *ctx) { - Client &client = *(Client *)ctx; + auto &r = *(Response *)ctx; - tag_print(client, type, value); + tag_print(r, type, value); } CommandResult -handle_listfiles(Client &client, unsigned argc, char *argv[]) +handle_listfiles(Client &client, Request args, Response &r) { - const char *const uri = argc == 2 - ? argv[1] - /* default is root directory */ - : ""; - - if (memcmp(uri, "file:///", 8) == 0) - /* list local directory */ - return handle_listfiles_local(client, uri + 7); + /* default is root directory */ + const auto uri = args.GetOptional(0, ""); + Error error; + const auto located_uri = LocateUri(uri, &client, #ifdef ENABLE_DATABASE - if (uri_has_scheme(uri)) - /* use storage plugin to list remote directory */ - return handle_listfiles_storage(client, uri); + nullptr, +#endif + error); - /* must be a path relative to the configured - music_directory */ + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); - if (client.partition.instance.storage != nullptr) - /* if we have a storage instance, obtain a list of - files from it */ - return handle_listfiles_storage(client, - *client.partition.instance.storage, - uri); + case LocatedUri::Type::ABSOLUTE: +#ifdef ENABLE_DATABASE + /* use storage plugin to list remote directory */ + return handle_listfiles_storage(r, located_uri.canonical_uri); +#else + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; +#endif - /* fall back to entries from database if we have no storage */ - return handle_listfiles_db(client, uri); + case LocatedUri::Type::RELATIVE: +#ifdef ENABLE_DATABASE + if (client.partition.instance.storage != nullptr) + /* if we have a storage instance, obtain a list of + files from it */ + return handle_listfiles_storage(r, + *client.partition.instance.storage, + uri); + + /* fall back to entries from database if we have no storage */ + return handle_listfiles_db(client, r, uri); #else - command_error(client, ACK_ERROR_NO_EXIST, "No database"); - return CommandResult::ERROR; + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; #endif + + case LocatedUri::Type::PATH: + /* list local directory */ + return handle_listfiles_local(r, located_uri.canonical_uri, + located_uri.path); + } + + gcc_unreachable(); } static constexpr tag_handler print_tag_handler = { @@ -156,69 +174,35 @@ static constexpr tag_handler print_tag_handler = { nullptr, }; -CommandResult -handle_lsinfo(Client &client, unsigned argc, char *argv[]) +static CommandResult +handle_lsinfo_absolute(Response &r, const char *uri) { - const char *const uri = argc == 2 - ? argv[1] - /* default is root directory */ - : ""; - - if (memcmp(uri, "file:///", 8) == 0) { - /* print information about an arbitrary local file */ - const char *path_utf8 = uri + 7; - const auto path_fs = AllocatedPath::FromUTF8(path_utf8); - - if (path_fs.IsNull()) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported file name"); - return CommandResult::ERROR; - } - - Error error; - if (!client.AllowFile(path_fs, error)) - return print_error(client, error); - - DetachedSong song(path_utf8); - if (!song.Update()) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such file"); - return CommandResult::ERROR; - } - - song_print_info(client, song); - return CommandResult::OK; + if (!tag_stream_scan(uri, print_tag_handler, &r)) { + r.Error(ACK_ERROR_NO_EXIST, "No such file"); + return CommandResult::ERROR; } - if (uri_has_scheme(uri)) { - if (!uri_supported_scheme(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, - "unsupported URI scheme"); - return CommandResult::ERROR; - } - - if (!tag_stream_scan(uri, print_tag_handler, &client)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such file"); - return CommandResult::ERROR; - } - - return CommandResult::OK; - } + return CommandResult::OK; +} +static CommandResult +handle_lsinfo_relative(Client &client, Response &r, const char *uri) +{ #ifdef ENABLE_DATABASE - CommandResult result = handle_lsinfo2(client, argc, argv); + CommandResult result = handle_lsinfo2(client, uri, r); if (result != CommandResult::OK) return result; +#else + (void)client; #endif if (isRootDirectory(uri)) { Error error; const auto &list = ListPlaylistFiles(error); - print_spl_list(client, list); + print_spl_list(r, list); } else { #ifndef ENABLE_DATABASE - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; #endif } @@ -226,38 +210,84 @@ handle_lsinfo(Client &client, unsigned argc, char *argv[]) return CommandResult::OK; } +static CommandResult +handle_lsinfo_path(Client &client, Response &r, + const char *path_utf8, Path path_fs) +{ + DetachedSong song(path_utf8); + if (!song.LoadFile(path_fs)) { + r.Error(ACK_ERROR_NO_EXIST, "No such file"); + return CommandResult::ERROR; + } + + song_print_info(r, client.partition, song); + return CommandResult::OK; +} + +CommandResult +handle_lsinfo(Client &client, Request args, Response &r) +{ + /* default is root directory */ + const auto uri = args.GetOptional(0, ""); + + Error error; + const auto located_uri = LocateUri(uri, &client, +#ifdef ENABLE_DATABASE + nullptr, +#endif + error); + + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); + + case LocatedUri::Type::ABSOLUTE: + return handle_lsinfo_absolute(r, located_uri.canonical_uri); + + case LocatedUri::Type::RELATIVE: + return handle_lsinfo_relative(client, r, + located_uri.canonical_uri); + + case LocatedUri::Type::PATH: + /* print information about an arbitrary local file */ + return handle_lsinfo_path(client, r, located_uri.canonical_uri, + located_uri.path); + } + + gcc_unreachable(); +} + #ifdef ENABLE_DATABASE static CommandResult -handle_update(Client &client, UpdateService &update, +handle_update(Response &r, UpdateService &update, const char *uri_utf8, bool discard) { unsigned ret = update.Enqueue(uri_utf8, discard); if (ret > 0) { - client_printf(client, "updating_db: %i\n", ret); + r.Format("updating_db: %i\n", ret); return CommandResult::OK; } else { - command_error(client, ACK_ERROR_UPDATE_ALREADY, - "already updating"); + r.Error(ACK_ERROR_UPDATE_ALREADY, "already updating"); return CommandResult::ERROR; } } static CommandResult -handle_update(Client &client, Database &db, +handle_update(Response &r, Database &db, const char *uri_utf8, bool discard) { Error error; unsigned id = db.Update(uri_utf8, discard, error); if (id > 0) { - client_printf(client, "updating_db: %i\n", id); + r.Format("updating_db: %i\n", id); return CommandResult::OK; } else if (error.IsDefined()) { - return print_error(client, error); + return print_error(r, error); } else { /* Database::Update() has returned 0 without setting the Error: the method is not implemented */ - command_error(client, ACK_ERROR_NO_EXIST, "Not implemented"); + r.Error(ACK_ERROR_NO_EXIST, "Not implemented"); return CommandResult::ERROR; } } @@ -265,72 +295,62 @@ handle_update(Client &client, Database &db, #endif static CommandResult -handle_update(Client &client, unsigned argc, char *argv[], bool discard) +handle_update(Client &client, Request args, Response &r, bool discard) { #ifdef ENABLE_DATABASE const char *path = ""; - assert(argc <= 2); - if (argc == 2) { - path = argv[1]; + assert(args.size <= 1); + if (!args.IsEmpty()) { + path = args.front(); - if (*path == 0 || strcmp(path, "/") == 0) + if (*path == 0 || StringIsEqual(path, "/")) /* backwards compatibility with MPD 0.15 */ path = ""; else if (!uri_safe_local(path)) { - command_error(client, ACK_ERROR_ARG, - "Malformed path"); + r.Error(ACK_ERROR_ARG, "Malformed path"); return CommandResult::ERROR; } } UpdateService *update = client.partition.instance.update; if (update != nullptr) - return handle_update(client, *update, path, discard); + return handle_update(r, *update, path, discard); Database *db = client.partition.instance.database; if (db != nullptr) - return handle_update(client, *db, path, discard); + return handle_update(r, *db, path, discard); #else - (void)argc; - (void)argv; + (void)client; + (void)args; (void)discard; #endif - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } CommandResult -handle_update(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_update(Client &client, Request args, gcc_unused Response &r) { - return handle_update(client, argc, argv, false); + return handle_update(client, args, r, false); } CommandResult -handle_rescan(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rescan(Client &client, Request args, Response &r) { - return handle_update(client, argc, argv, true); + return handle_update(client, args, r, true); } CommandResult -handle_setvol(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_setvol(Client &client, Request args, Response &r) { unsigned level; - bool success; - - if (!check_unsigned(client, &level, argv[1])) + if (!args.Parse(0, level, r, 100)) return CommandResult::ERROR; - if (level > 100) { - command_error(client, ACK_ERROR_ARG, "Invalid volume value"); - return CommandResult::ERROR; - } - - success = volume_level_change(client.partition.outputs, level); - if (!success) { - command_error(client, ACK_ERROR_SYSTEM, - "problems setting volume"); + if (!volume_level_change(client.partition.outputs, level)) { + r.Error(ACK_ERROR_SYSTEM, "problems setting volume"); return CommandResult::ERROR; } @@ -338,20 +358,15 @@ handle_setvol(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_volume(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_volume(Client &client, Request args, Response &r) { int relative; - if (!check_int(client, &relative, argv[1])) + if (!args.Parse(0, relative, r, -100, 100)) return CommandResult::ERROR; - if (relative < -100 || relative > 100) { - command_error(client, ACK_ERROR_ARG, "Invalid volume value"); - return CommandResult::ERROR; - } - const int old_volume = volume_level_get(client.partition.outputs); if (old_volume < 0) { - command_error(client, ACK_ERROR_SYSTEM, "No mixer"); + r.Error(ACK_ERROR_SYSTEM, "No mixer"); return CommandResult::ERROR; } @@ -363,8 +378,7 @@ handle_volume(Client &client, gcc_unused unsigned argc, char *argv[]) if (new_volume != old_volume && !volume_level_change(client.partition.outputs, new_volume)) { - command_error(client, ACK_ERROR_SYSTEM, - "problems setting volume"); + r.Error(ACK_ERROR_SYSTEM, "problems setting volume"); return CommandResult::ERROR; } @@ -372,27 +386,25 @@ handle_volume(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_stats(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_stats(Client &client, gcc_unused Request args, Response &r) { - stats_print(client); + stats_print(r, client.partition); return CommandResult::OK; } CommandResult -handle_ping(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_ping(gcc_unused Client &client, gcc_unused Request args, + gcc_unused Response &r) { return CommandResult::OK; } CommandResult -handle_password(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_password(Client &client, Request args, Response &r) { unsigned permission = 0; - - if (getPermissionFromPassword(argv[1], &permission) < 0) { - command_error(client, ACK_ERROR_PASSWORD, "incorrect password"); + if (getPermissionFromPassword(args.front(), &permission) < 0) { + r.Error(ACK_ERROR_PASSWORD, "incorrect password"); return CommandResult::ERROR; } @@ -402,12 +414,11 @@ handle_password(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_config(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_config(Client &client, gcc_unused Request args, Response &r) { if (!client.IsLocal()) { - command_error(client, ACK_ERROR_PERMISSION, - "Command only permitted to local clients"); + r.Error(ACK_ERROR_PERMISSION, + "Command only permitted to local clients"); return CommandResult::ERROR; } @@ -415,7 +426,7 @@ handle_config(Client &client, const Storage *storage = client.GetStorage(); if (storage != nullptr) { const auto path = storage->MapUTF8(""); - client_printf(client, "music_directory: %s\n", path.c_str()); + r.Format("music_directory: %s\n", path.c_str()); } #endif @@ -423,17 +434,14 @@ handle_config(Client &client, } CommandResult -handle_idle(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_idle(Client &client, Request args, Response &r) { unsigned flags = 0; - - for (unsigned i = 1; i < argc; ++i) { - unsigned event = idle_parse_name(argv[i]); + for (const char *i : args) { + unsigned event = idle_parse_name(i); if (event == 0) { - command_error(client, ACK_ERROR_ARG, - "Unrecognized idle event: %s", - argv[i]); + r.FormatError(ACK_ERROR_ARG, + "Unrecognized idle event: %s", i); return CommandResult::ERROR; } diff --git a/src/command/OtherCommands.hxx b/src/command/OtherCommands.hxx index 7cfa35dfb..2a918df97 100644 --- a/src/command/OtherCommands.hxx +++ b/src/command/OtherCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,53 +23,55 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_urlhandlers(Client &client, unsigned argc, char *argv[]); +handle_urlhandlers(Client &client, Request request, Response &response); CommandResult -handle_decoders(Client &client, unsigned argc, char *argv[]); +handle_decoders(Client &client, Request request, Response &response); CommandResult -handle_tagtypes(Client &client, unsigned argc, char *argv[]); +handle_tagtypes(Client &client, Request request, Response &response); CommandResult -handle_kill(Client &client, unsigned argc, char *argv[]); +handle_kill(Client &client, Request request, Response &response); CommandResult -handle_close(Client &client, unsigned argc, char *argv[]); +handle_close(Client &client, Request request, Response &response); CommandResult -handle_listfiles(Client &client, unsigned argc, char *argv[]); +handle_listfiles(Client &client, Request request, Response &response); CommandResult -handle_lsinfo(Client &client, unsigned argc, char *argv[]); +handle_lsinfo(Client &client, Request request, Response &response); CommandResult -handle_update(Client &client, unsigned argc, char *argv[]); +handle_update(Client &client, Request request, Response &response); CommandResult -handle_rescan(Client &client, unsigned argc, char *argv[]); +handle_rescan(Client &client, Request request, Response &response); CommandResult -handle_setvol(Client &client, unsigned argc, char *argv[]); +handle_setvol(Client &client, Request request, Response &response); CommandResult -handle_volume(Client &client, unsigned argc, char *argv[]); +handle_volume(Client &client, Request request, Response &response); CommandResult -handle_stats(Client &client, unsigned argc, char *argv[]); +handle_stats(Client &client, Request request, Response &response); CommandResult -handle_ping(Client &client, unsigned argc, char *argv[]); +handle_ping(Client &client, Request request, Response &response); CommandResult -handle_password(Client &client, unsigned argc, char *argv[]); +handle_password(Client &client, Request request, Response &response); CommandResult -handle_config(Client &client, unsigned argc, char *argv[]); +handle_config(Client &client, Request request, Response &response); CommandResult -handle_idle(Client &client, unsigned argc, char *argv[]); +handle_idle(Client &client, Request request, Response &response); #endif diff --git a/src/command/OutputCommands.cxx b/src/command/OutputCommands.cxx index c69a0dd65..7bbe5f905 100644 --- a/src/command/OutputCommands.cxx +++ b/src/command/OutputCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,23 +19,24 @@ #include "config.h" #include "OutputCommands.hxx" +#include "Request.hxx" #include "output/OutputPrint.hxx" #include "output/OutputCommand.hxx" -#include "protocol/Result.hxx" -#include "protocol/ArgParser.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" +#include "util/ConstBuffer.hxx" CommandResult -handle_enableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_enableoutput(Client &client, Request args, Response &r) { + assert(args.size == 1); unsigned device; - if (!check_unsigned(client, &device, argv[1])) + if (!args.Parse(0, device, r)) return CommandResult::ERROR; if (!audio_output_enable_index(client.partition.outputs, device)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such audio output"); + r.Error(ACK_ERROR_NO_EXIST, "No such audio output"); return CommandResult::ERROR; } @@ -43,15 +44,15 @@ handle_enableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_disableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_disableoutput(Client &client, Request args, Response &r) { + assert(args.size == 1); unsigned device; - if (!check_unsigned(client, &device, argv[1])) + if (!args.Parse(0, device, r)) return CommandResult::ERROR; if (!audio_output_disable_index(client.partition.outputs, device)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such audio output"); + r.Error(ACK_ERROR_NO_EXIST, "No such audio output"); return CommandResult::ERROR; } @@ -59,15 +60,15 @@ handle_disableoutput(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_toggleoutput(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_toggleoutput(Client &client, Request args, Response &r) { + assert(args.size == 1); unsigned device; - if (!check_unsigned(client, &device, argv[1])) + if (!args.Parse(0, device, r)) return CommandResult::ERROR; if (!audio_output_toggle_index(client.partition.outputs, device)) { - command_error(client, ACK_ERROR_NO_EXIST, - "No such audio output"); + r.Error(ACK_ERROR_NO_EXIST, "No such audio output"); return CommandResult::ERROR; } @@ -75,10 +76,10 @@ handle_toggleoutput(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_devices(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_devices(Client &client, gcc_unused Request args, Response &r) { - printAudioDevices(client, client.partition.outputs); + assert(args.IsEmpty()); + printAudioDevices(r, client.partition.outputs); return CommandResult::OK; } diff --git a/src/command/OutputCommands.hxx b/src/command/OutputCommands.hxx index 8d6be0511..3dd81bc23 100644 --- a/src/command/OutputCommands.hxx +++ b/src/command/OutputCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,17 +23,19 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_enableoutput(Client &client, unsigned argc, char *argv[]); +handle_enableoutput(Client &client, Request request, Response &response); CommandResult -handle_disableoutput(Client &client, unsigned argc, char *argv[]); +handle_disableoutput(Client &client, Request request, Response &response); CommandResult -handle_toggleoutput(Client &client, unsigned argc, char *argv[]); +handle_toggleoutput(Client &client, Request request, Response &response); CommandResult -handle_devices(Client &client, unsigned argc, char *argv[]); +handle_devices(Client &client, Request request, Response &response); #endif diff --git a/src/command/PlayerCommands.cxx b/src/command/PlayerCommands.cxx index cd7f42289..11cde2e98 100644 --- a/src/command/PlayerCommands.cxx +++ b/src/command/PlayerCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,17 +19,18 @@ #include "config.h" #include "PlayerCommands.hxx" +#include "Request.hxx" #include "CommandError.hxx" #include "queue/Playlist.hxx" #include "PlaylistPrint.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "mixer/Volume.hxx" #include "Partition.hxx" #include "Instance.hxx" -#include "protocol/Result.hxx" -#include "protocol/ArgParser.hxx" #include "AudioFormat.hxx" #include "ReplayGainConfig.hxx" +#include "util/ConstBuffer.hxx" #ifdef ENABLE_DATABASE #include "db/update/Service.hxx" @@ -56,51 +57,47 @@ #define COMMAND_STATUS_UPDATING_DB "updating_db" CommandResult -handle_play(Client &client, unsigned argc, char *argv[]) +handle_play(Client &client, Request args, Response &r) { int song = -1; - - if (argc == 2 && !check_int(client, &song, argv[1])) + if (!args.ParseOptional(0, song, r)) return CommandResult::ERROR; + PlaylistResult result = client.partition.PlayPosition(song); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_playid(Client &client, unsigned argc, char *argv[]) +handle_playid(Client &client, Request args, Response &r) { int id = -1; - - if (argc == 2 && !check_int(client, &id, argv[1])) + if (!args.ParseOptional(0, id, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.PlayId(id); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_stop(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_stop(Client &client, gcc_unused Request args, gcc_unused Response &r) { client.partition.Stop(); return CommandResult::OK; } CommandResult -handle_currentsong(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_currentsong(Client &client, gcc_unused Request args, Response &r) { - playlist_print_current(client, client.playlist); + playlist_print_current(r, client.partition, client.playlist); return CommandResult::OK; } CommandResult -handle_pause(Client &client, - unsigned argc, char *argv[]) +handle_pause(Client &client, Request args, Response &r) { - if (argc == 2) { + if (!args.IsEmpty()) { bool pause_flag; - if (!check_bool(client, &pause_flag, argv[1])) + if (!args.Parse(0, pause_flag, r)) return CommandResult::ERROR; client.player_control.SetPause(pause_flag); @@ -111,8 +108,7 @@ handle_pause(Client &client, } CommandResult -handle_status(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_status(Client &client, gcc_unused Request args, Response &r) { const char *state = nullptr; int song; @@ -132,63 +128,61 @@ handle_status(Client &client, } const playlist &playlist = client.playlist; - client_printf(client, - "volume: %i\n" - COMMAND_STATUS_REPEAT ": %i\n" - COMMAND_STATUS_RANDOM ": %i\n" - COMMAND_STATUS_SINGLE ": %i\n" - COMMAND_STATUS_CONSUME ": %i\n" - COMMAND_STATUS_PLAYLIST ": %li\n" - COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n" - COMMAND_STATUS_MIXRAMPDB ": %f\n" - COMMAND_STATUS_STATE ": %s\n", - volume_level_get(client.partition.outputs), - playlist.GetRepeat(), - playlist.GetRandom(), - playlist.GetSingle(), - playlist.GetConsume(), - (unsigned long)playlist.GetVersion(), - playlist.GetLength(), - client.player_control.GetMixRampDb(), - state); + r.Format("volume: %i\n" + COMMAND_STATUS_REPEAT ": %i\n" + COMMAND_STATUS_RANDOM ": %i\n" + COMMAND_STATUS_SINGLE ": %i\n" + COMMAND_STATUS_CONSUME ": %i\n" + COMMAND_STATUS_PLAYLIST ": %li\n" + COMMAND_STATUS_PLAYLIST_LENGTH ": %i\n" + COMMAND_STATUS_MIXRAMPDB ": %f\n" + COMMAND_STATUS_STATE ": %s\n", + volume_level_get(client.partition.outputs), + playlist.GetRepeat(), + playlist.GetRandom(), + playlist.GetSingle(), + playlist.GetConsume(), + (unsigned long)playlist.GetVersion(), + playlist.GetLength(), + client.player_control.GetMixRampDb(), + state); if (client.player_control.GetCrossFade() > 0) - client_printf(client, - COMMAND_STATUS_CROSSFADE ": %i\n", - int(client.player_control.GetCrossFade() + 0.5)); + r.Format(COMMAND_STATUS_CROSSFADE ": %i\n", + int(client.player_control.GetCrossFade() + 0.5)); if (client.player_control.GetMixRampDelay() > 0) - client_printf(client, - COMMAND_STATUS_MIXRAMPDELAY ": %f\n", - client.player_control.GetMixRampDelay()); + r.Format(COMMAND_STATUS_MIXRAMPDELAY ": %f\n", + client.player_control.GetMixRampDelay()); song = playlist.GetCurrentPosition(); if (song >= 0) { - client_printf(client, - COMMAND_STATUS_SONG ": %i\n" - COMMAND_STATUS_SONGID ": %u\n", - song, playlist.PositionToId(song)); + r.Format(COMMAND_STATUS_SONG ": %i\n" + COMMAND_STATUS_SONGID ": %u\n", + song, playlist.PositionToId(song)); } if (player_status.state != PlayerState::STOP) { - client_printf(client, - COMMAND_STATUS_TIME ": %i:%i\n" - "elapsed: %1.3f\n" - COMMAND_STATUS_BITRATE ": %u\n", - player_status.elapsed_time.RoundS(), - player_status.total_time.IsNegative() - ? 0u - : unsigned(player_status.total_time.RoundS()), - player_status.elapsed_time.ToDoubleS(), - player_status.bit_rate); + r.Format(COMMAND_STATUS_TIME ": %i:%i\n" + "elapsed: %1.3f\n" + COMMAND_STATUS_BITRATE ": %u\n", + player_status.elapsed_time.RoundS(), + player_status.total_time.IsNegative() + ? 0u + : unsigned(player_status.total_time.RoundS()), + player_status.elapsed_time.ToDoubleS(), + player_status.bit_rate); + + if (!player_status.total_time.IsNegative()) + r.Format("duration: %1.3f\n", + player_status.total_time.ToDoubleS()); if (player_status.audio_format.IsDefined()) { struct audio_format_string af_string; - client_printf(client, - COMMAND_STATUS_AUDIO ": %s\n", - audio_format_to_string(player_status.audio_format, - &af_string)); + r.Format(COMMAND_STATUS_AUDIO ": %s\n", + audio_format_to_string(player_status.audio_format, + &af_string)); } } @@ -198,32 +192,27 @@ handle_status(Client &client, ? update_service->GetId() : 0; if (updateJobId != 0) { - client_printf(client, - COMMAND_STATUS_UPDATING_DB ": %i\n", - updateJobId); + r.Format(COMMAND_STATUS_UPDATING_DB ": %i\n", + updateJobId); } #endif Error error = client.player_control.LockGetError(); if (error.IsDefined()) - client_printf(client, - COMMAND_STATUS_ERROR ": %s\n", - error.GetMessage()); + r.Format(COMMAND_STATUS_ERROR ": %s\n", + error.GetMessage()); song = playlist.GetNextPosition(); - if (song >= 0) { - client_printf(client, - COMMAND_STATUS_NEXTSONG ": %i\n" - COMMAND_STATUS_NEXTSONGID ": %u\n", - song, playlist.PositionToId(song)); - } + if (song >= 0) + r.Format(COMMAND_STATUS_NEXTSONG ": %i\n" + COMMAND_STATUS_NEXTSONGID ": %u\n", + song, playlist.PositionToId(song)); return CommandResult::OK; } CommandResult -handle_next(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_next(Client &client, gcc_unused Request args, gcc_unused Response &r) { playlist &playlist = client.playlist; @@ -239,18 +228,18 @@ handle_next(Client &client, } CommandResult -handle_previous(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_previous(Client &client, gcc_unused Request args, + gcc_unused Response &r) { client.partition.PlayPrevious(); return CommandResult::OK; } CommandResult -handle_repeat(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_repeat(Client &client, Request args, Response &r) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetRepeat(status); @@ -258,10 +247,10 @@ handle_repeat(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_single(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_single(Client &client, Request args, Response &r) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetSingle(status); @@ -269,10 +258,10 @@ handle_single(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_consume(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_consume(Client &client, Request args, Response &r) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetConsume(status); @@ -280,10 +269,10 @@ handle_consume(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_random(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_random(Client &client, Request args, Response &r) { bool status; - if (!check_bool(client, &status, argv[1])) + if (!args.Parse(0, status, r)) return CommandResult::ERROR; client.partition.SetRandom(status); @@ -292,102 +281,94 @@ handle_random(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_clearerror(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_clearerror(Client &client, gcc_unused Request args, + gcc_unused Response &r) { client.player_control.ClearError(); return CommandResult::OK; } CommandResult -handle_seek(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_seek(Client &client, Request args, Response &r) { unsigned song; SongTime seek_time; - - if (!check_unsigned(client, &song, argv[1])) - return CommandResult::ERROR; - if (!ParseCommandArg(client, seek_time, argv[2])) + if (!args.Parse(0, song, r) || !args.Parse(1, seek_time, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SeekSongPosition(song, seek_time); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_seekid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_seekid(Client &client, Request args, Response &r) { unsigned id; SongTime seek_time; - - if (!check_unsigned(client, &id, argv[1])) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; - if (!ParseCommandArg(client, seek_time, argv[2])) + if (!args.Parse(1, seek_time, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SeekSongId(id, seek_time); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_seekcur(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_seekcur(Client &client, Request args, Response &r) { - const char *p = argv[1]; + const char *p = args.front(); bool relative = *p == '+' || *p == '-'; SignedSongTime seek_time; - if (!ParseCommandArg(client, seek_time, p)) + if (!ParseCommandArg(r, seek_time, p)) return CommandResult::ERROR; PlaylistResult result = client.partition.SeekCurrent(seek_time, relative); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_crossfade(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_crossfade(Client &client, Request args, Response &r) { unsigned xfade_time; - - if (!check_unsigned(client, &xfade_time, argv[1])) + if (!args.Parse(0, xfade_time, r)) return CommandResult::ERROR; - client.player_control.SetCrossFade(xfade_time); + client.player_control.SetCrossFade(xfade_time); return CommandResult::OK; } CommandResult -handle_mixrampdb(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_mixrampdb(Client &client, Request args, Response &r) { float db; - - if (!check_float(client, &db, argv[1])) + if (!args.Parse(0, db, r)) return CommandResult::ERROR; - client.player_control.SetMixRampDb(db); + client.player_control.SetMixRampDb(db); return CommandResult::OK; } CommandResult -handle_mixrampdelay(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_mixrampdelay(Client &client, Request args, Response &r) { float delay_secs; - - if (!check_float(client, &delay_secs, argv[1])) + if (!args.Parse(0, delay_secs, r)) return CommandResult::ERROR; + client.player_control.SetMixRampDelay(delay_secs); return CommandResult::OK; } CommandResult -handle_replay_gain_mode(Client &client, - gcc_unused unsigned argc, char *argv[]) +handle_replay_gain_mode(Client &client, Request args, Response &r) { - if (!replay_gain_set_mode_string(argv[1])) { - command_error(client, ACK_ERROR_ARG, - "Unrecognized replay gain mode"); + if (!replay_gain_set_mode_string(args.front())) { + r.Error(ACK_ERROR_ARG, "Unrecognized replay gain mode"); return CommandResult::ERROR; } @@ -396,10 +377,9 @@ handle_replay_gain_mode(Client &client, } CommandResult -handle_replay_gain_status(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_replay_gain_status(gcc_unused Client &client, gcc_unused Request args, + Response &r) { - client_printf(client, "replay_gain_mode: %s\n", - replay_gain_get_mode_string()); + r.Format("replay_gain_mode: %s\n", replay_gain_get_mode_string()); return CommandResult::OK; } diff --git a/src/command/PlayerCommands.hxx b/src/command/PlayerCommands.hxx index da7083f1e..76ce51ef5 100644 --- a/src/command/PlayerCommands.hxx +++ b/src/command/PlayerCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,68 +23,70 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_play(Client &client, unsigned argc, char *argv[]); +handle_play(Client &client, Request request, Response &response); CommandResult -handle_playid(Client &client, unsigned argc, char *argv[]); +handle_playid(Client &client, Request request, Response &response); CommandResult -handle_stop(Client &client, unsigned argc, char *argv[]); +handle_stop(Client &client, Request request, Response &response); CommandResult -handle_currentsong(Client &client, unsigned argc, char *argv[]); +handle_currentsong(Client &client, Request request, Response &response); CommandResult -handle_pause(Client &client, unsigned argc, char *argv[]); +handle_pause(Client &client, Request request, Response &response); CommandResult -handle_status(Client &client, unsigned argc, char *argv[]); +handle_status(Client &client, Request request, Response &response); CommandResult -handle_next(Client &client, unsigned argc, char *argv[]); +handle_next(Client &client, Request request, Response &response); CommandResult -handle_previous(Client &client, unsigned argc, char *avg[]); +handle_previous(Client &client, Request request, Response &response); CommandResult -handle_repeat(Client &client, unsigned argc, char *argv[]); +handle_repeat(Client &client, Request request, Response &response); CommandResult -handle_single(Client &client, unsigned argc, char *argv[]); +handle_single(Client &client, Request request, Response &response); CommandResult -handle_consume(Client &client, unsigned argc, char *argv[]); +handle_consume(Client &client, Request request, Response &response); CommandResult -handle_random(Client &client, unsigned argc, char *argv[]); +handle_random(Client &client, Request request, Response &response); CommandResult -handle_clearerror(Client &client, unsigned argc, char *argv[]); +handle_clearerror(Client &client, Request request, Response &response); CommandResult -handle_seek(Client &client, unsigned argc, char *argv[]); +handle_seek(Client &client, Request request, Response &response); CommandResult -handle_seekid(Client &client, unsigned argc, char *argv[]); +handle_seekid(Client &client, Request request, Response &response); CommandResult -handle_seekcur(Client &client, unsigned argc, char *argv[]); +handle_seekcur(Client &client, Request request, Response &response); CommandResult -handle_crossfade(Client &client, unsigned argc, char *argv[]); +handle_crossfade(Client &client, Request request, Response &response); CommandResult -handle_mixrampdb(Client &client, unsigned argc, char *argv[]); +handle_mixrampdb(Client &client, Request request, Response &response); CommandResult -handle_mixrampdelay(Client &client, unsigned argc, char *argv[]); +handle_mixrampdelay(Client &client, Request request, Response &response); CommandResult -handle_replay_gain_mode(Client &client, unsigned argc, char *argv[]); +handle_replay_gain_mode(Client &client, Request request, Response &response); CommandResult -handle_replay_gain_status(Client &client, unsigned argc, char *argv[]); +handle_replay_gain_status(Client &client, Request request, Response &response); #endif diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index c2b18064c..625e82055 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "PlaylistCommands.hxx" +#include "Request.hxx" #include "db/DatabasePlaylist.hxx" #include "CommandError.hxx" #include "PlaylistPrint.hxx" @@ -32,143 +33,157 @@ #include "queue/Playlist.hxx" #include "TimePrint.hxx" #include "client/Client.hxx" -#include "protocol/ArgParser.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "ls.hxx" +#include "Mapper.hxx" +#include "fs/AllocatedPath.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" + +bool +playlist_commands_available() +{ + return !map_spl_path().IsNull(); +} static void -print_spl_list(Client &client, const PlaylistVector &list) +print_spl_list(Response &r, const PlaylistVector &list) { for (const auto &i : list) { - client_printf(client, "playlist: %s\n", i.name.c_str()); + r.Format("playlist: %s\n", i.name.c_str()); if (i.mtime > 0) - time_print(client, "Last-Modified", i.mtime); + time_print(r, "Last-Modified", i.mtime); } } CommandResult -handle_save(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_save(Client &client, Request args, Response &r) { - PlaylistResult result = spl_save_playlist(argv[1], client.playlist); - return print_playlist_result(client, result); + Error error; + return spl_save_playlist(args.front(), client.playlist, error) + ? CommandResult::OK + : print_error(r, error); } CommandResult -handle_load(Client &client, unsigned argc, char *argv[]) +handle_load(Client &client, Request args, Response &r) { - unsigned start_index, end_index; - - if (argc < 3) { - start_index = 0; - end_index = unsigned(-1); - } else if (!check_range(client, &start_index, &end_index, argv[2])) + RangeArg range = RangeArg::All(); + if (!args.ParseOptional(1, range, r)) return CommandResult::ERROR; const ScopeBulkEdit bulk_edit(client.partition); Error error; const SongLoader loader(client); - if (!playlist_open_into_queue(argv[1], - start_index, end_index, + if (!playlist_open_into_queue(args.front(), + range.start, range.end, client.playlist, client.player_control, loader, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } CommandResult -handle_listplaylist(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_listplaylist(Client &client, Request args, Response &r) { - if (playlist_file_print(client, argv[1], false)) + const char *const name = args.front(); + + if (playlist_file_print(r, client.partition, SongLoader(client), + name, false)) return CommandResult::OK; Error error; - return spl_print(client, argv[1], false, error) + return spl_print(r, client.partition, name, false, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_listplaylistinfo(Client &client, - gcc_unused unsigned argc, char *argv[]) +handle_listplaylistinfo(Client &client, Request args, Response &r) { - if (playlist_file_print(client, argv[1], true)) + const char *const name = args.front(); + + if (playlist_file_print(r, client.partition, SongLoader(client), + name, true)) return CommandResult::OK; Error error; - return spl_print(client, argv[1], true, error) + return spl_print(r, client.partition, name, true, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_rm(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rm(gcc_unused Client &client, Request args, Response &r) { + const char *const name = args.front(); + Error error; - return spl_delete(argv[1], error) + return spl_delete(name, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_rename(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rename(gcc_unused Client &client, Request args, Response &r) { + const char *const old_name = args[0]; + const char *const new_name = args[1]; + Error error; - return spl_rename(argv[1], argv[2], error) + return spl_rename(old_name, new_name, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_playlistdelete(Client &client, - gcc_unused unsigned argc, char *argv[]) { - char *playlist = argv[1]; +handle_playlistdelete(gcc_unused Client &client, Request args, Response &r) +{ + const char *const name = args[0]; unsigned from; - - if (!check_unsigned(client, &from, argv[2])) + if (!args.Parse(1, from, r)) return CommandResult::ERROR; Error error; - return spl_remove_index(playlist, from, error) + return spl_remove_index(name, from, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_playlistmove(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_playlistmove(gcc_unused Client &client, Request args, Response &r) { - char *playlist = argv[1]; + const char *const name = args.front(); unsigned from, to; - - if (!check_unsigned(client, &from, argv[2])) - return CommandResult::ERROR; - if (!check_unsigned(client, &to, argv[3])) + if (!args.Parse(1, from, r) || !args.Parse(2, to, r)) return CommandResult::ERROR; Error error; - return spl_move_index(playlist, from, to, error) + return spl_move_index(name, from, to, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_playlistclear(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_playlistclear(gcc_unused Client &client, Request args, Response &r) { + const char *const name = args.front(); + Error error; - return spl_clear(argv[1], error) + return spl_clear(name, error) ? CommandResult::OK - : print_error(client, error); + : print_error(r, error); } CommandResult -handle_playlistadd(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_playlistadd(Client &client, Request args, Response &r) { - char *playlist = argv[1]; - char *uri = argv[2]; + const char *const playlist = args[0]; + const char *const uri = args[1]; bool success; Error error; @@ -179,7 +194,7 @@ handle_playlistadd(Client &client, gcc_unused unsigned argc, char *argv[]) #ifdef ENABLE_DATABASE const Database *db = client.GetDatabase(error); if (db == nullptr) - return print_error(client, error); + return print_error(r, error); success = search_add_to_playlist(*db, *client.GetStorage(), uri, playlist, nullptr, @@ -190,23 +205,22 @@ handle_playlistadd(Client &client, gcc_unused unsigned argc, char *argv[]) } if (!success && !error.IsDefined()) { - command_error(client, ACK_ERROR_NO_EXIST, - "directory or file not found"); + r.Error(ACK_ERROR_NO_EXIST, "directory or file not found"); return CommandResult::ERROR; } - return success ? CommandResult::OK : print_error(client, error); + return success ? CommandResult::OK : print_error(r, error); } CommandResult -handle_listplaylists(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_listplaylists(gcc_unused Client &client, gcc_unused Request args, + Response &r) { Error error; const auto list = ListPlaylistFiles(error); if (list.empty() && error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - print_spl_list(client, list); + print_spl_list(r, list); return CommandResult::OK; } diff --git a/src/command/PlaylistCommands.hxx b/src/command/PlaylistCommands.hxx index fba4e1318..9f263df62 100644 --- a/src/command/PlaylistCommands.hxx +++ b/src/command/PlaylistCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,40 +21,47 @@ #define MPD_PLAYLIST_COMMANDS_HXX #include "CommandResult.hxx" +#include "Compiler.h" class Client; +class Request; +class Response; + +gcc_const +bool +playlist_commands_available(); CommandResult -handle_save(Client &client, unsigned argc, char *argv[]); +handle_save(Client &client, Request request, Response &response); CommandResult -handle_load(Client &client, unsigned argc, char *argv[]); +handle_load(Client &client, Request request, Response &response); CommandResult -handle_listplaylist(Client &client, unsigned argc, char *argv[]); +handle_listplaylist(Client &client, Request request, Response &response); CommandResult -handle_listplaylistinfo(Client &client, unsigned argc, char *argv[]); +handle_listplaylistinfo(Client &client, Request request, Response &response); CommandResult -handle_rm(Client &client, unsigned argc, char *argv[]); +handle_rm(Client &client, Request request, Response &response); CommandResult -handle_rename(Client &client, unsigned argc, char *argv[]); +handle_rename(Client &client, Request request, Response &response); CommandResult -handle_playlistdelete(Client &client, unsigned argc, char *argv[]); +handle_playlistdelete(Client &client, Request request, Response &response); CommandResult -handle_playlistmove(Client &client, unsigned argc, char *argv[]); +handle_playlistmove(Client &client, Request request, Response &response); CommandResult -handle_playlistclear(Client &client, unsigned argc, char *argv[]); +handle_playlistclear(Client &client, Request request, Response &response); CommandResult -handle_playlistadd(Client &client, unsigned argc, char *argv[]); +handle_playlistadd(Client &client, Request request, Response &response); CommandResult -handle_listplaylists(Client &client, unsigned argc, char *argv[]); +handle_listplaylists(Client &client, Request request, Response &response); #endif diff --git a/src/command/QueueCommands.cxx b/src/command/QueueCommands.cxx index d0b789eb1..7751aa26d 100644 --- a/src/command/QueueCommands.cxx +++ b/src/command/QueueCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,19 +19,20 @@ #include "config.h" #include "QueueCommands.hxx" +#include "Request.hxx" #include "CommandError.hxx" #include "db/DatabaseQueue.hxx" #include "db/Selection.hxx" #include "SongFilter.hxx" #include "SongLoader.hxx" +#include "DetachedSong.hxx" +#include "LocateUri.hxx" #include "queue/Playlist.hxx" #include "PlaylistPrint.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "BulkEdit.hxx" -#include "protocol/ArgParser.hxx" -#include "protocol/Result.hxx" -#include "ls.hxx" #include "util/ConstBuffer.hxx" #include "util/UriUtil.hxx" #include "util/NumberParser.hxx" @@ -42,26 +43,48 @@ #include <string.h> -static const char * -translate_uri(Client &client, const char *uri) +static CommandResult +AddUri(Client &client, const LocatedUri &uri, Response &r) { - if (memcmp(uri, "file:///", 8) == 0) - /* drop the "file://", leave only an absolute path - (starting with a slash) */ - return uri + 7; - - if (PathTraitsUTF8::IsAbsolute(uri)) { - command_error(client, ACK_ERROR_NO_EXIST, "Malformed URI"); - return nullptr; - } + Error error; + DetachedSong *song = SongLoader(client).LoadSong(uri, error); + if (song == nullptr) + return print_error(r, error); + + auto &partition = client.partition; + unsigned id = partition.playlist.AppendSong(partition.pc, + std::move(*song), error); + delete song; + if (id == 0) + return print_error(r, error); + + return CommandResult::OK; +} + +static CommandResult +AddDatabaseSelection(Client &client, const char *uri, Response &r) +{ +#ifdef ENABLE_DATABASE + const ScopeBulkEdit bulk_edit(client.partition); + + const DatabaseSelection selection(uri, true); + Error error; + return AddFromDatabase(client.partition, selection, error) + ? CommandResult::OK + : print_error(r, error); +#else + (void)client; + (void)uri; - return uri; + r.Error(ACK_ERROR_NO_EXIST, "No database"); + return CommandResult::ERROR; +#endif } CommandResult -handle_add(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_add(Client &client, Request args, Response &r) { - const char *uri = argv[1]; + const char *uri = args.front(); if (memcmp(uri, "/", 2) == 0) /* this URI is malformed, but some clients are buggy and use "add /" to add the whole database, which @@ -70,61 +93,54 @@ handle_add(Client &client, gcc_unused unsigned argc, char *argv[]) here */ uri = ""; - uri = translate_uri(client, uri); - if (uri == nullptr) - return CommandResult::ERROR; - - if (uri_has_scheme(uri) || PathTraitsUTF8::IsAbsolute(uri)) { - const SongLoader loader(client); - Error error; - unsigned id = client.partition.AppendURI(loader, uri, error); - if (id == 0) - return print_error(client, error); - - return CommandResult::OK; - } - -#ifdef ENABLE_DATABASE - const ScopeBulkEdit bulk_edit(client.partition); - - const DatabaseSelection selection(uri, true); Error error; - return AddFromDatabase(client.partition, selection, error) - ? CommandResult::OK - : print_error(client, error); -#else - command_error(client, ACK_ERROR_NO_EXIST, "No database"); - return CommandResult::ERROR; + const auto located_uri = LocateUri(uri, &client, +#ifdef ENABLE_DATABASE + nullptr, #endif + error); + switch (located_uri.type) { + case LocatedUri::Type::UNKNOWN: + return print_error(r, error); + + case LocatedUri::Type::ABSOLUTE: + case LocatedUri::Type::PATH: + return AddUri(client, located_uri, r); + + case LocatedUri::Type::RELATIVE: + return AddDatabaseSelection(client, located_uri.canonical_uri, + r); + } + + gcc_unreachable(); } CommandResult -handle_addid(Client &client, unsigned argc, char *argv[]) +handle_addid(Client &client, Request args, Response &r) { - const char *const uri = translate_uri(client, argv[1]); - if (uri == nullptr) - return CommandResult::ERROR; + const char *const uri = args.front(); const SongLoader loader(client); Error error; unsigned added_id = client.partition.AppendURI(loader, uri, error); if (added_id == 0) - return print_error(client, error); + return print_error(r, error); - if (argc == 3) { + if (args.size == 2) { unsigned to; - if (!check_unsigned(client, &to, argv[2])) + if (!args.Parse(1, to, r)) return CommandResult::ERROR; + PlaylistResult result = client.partition.MoveId(added_id, to); if (result != PlaylistResult::SUCCESS) { CommandResult ret = - print_playlist_result(client, result); + print_playlist_result(r, result); client.partition.DeleteId(added_id); return ret; } } - client_printf(client, "Id: %u\n", added_id); + r.Format("Id: %u\n", added_id); return CommandResult::OK; } @@ -160,15 +176,15 @@ parse_time_range(const char *p, SongTime &start_r, SongTime &end_r) } CommandResult -handle_rangeid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_rangeid(Client &client, Request args, Response &r) { unsigned id; - if (!check_unsigned(client, &id, argv[1])) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; SongTime start, end; - if (!parse_time_range(argv[2], start, end)) { - command_error(client, ACK_ERROR_ARG, "Bad range"); + if (!parse_time_range(args[1], start, end)) { + r.Error(ACK_ERROR_ARG, "Bad range"); return CommandResult::ERROR; } @@ -176,118 +192,110 @@ handle_rangeid(Client &client, gcc_unused unsigned argc, char *argv[]) if (!client.partition.playlist.SetSongIdRange(client.partition.pc, id, start, end, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } CommandResult -handle_delete(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_delete(Client &client, Request args, Response &r) { - unsigned start, end; - - if (!check_range(client, &start, &end, argv[1])) + RangeArg range; + if (!args.Parse(0, range, r)) return CommandResult::ERROR; - PlaylistResult result = client.partition.DeleteRange(start, end); - return print_playlist_result(client, result); + auto result = client.partition.DeleteRange(range.start, range.end); + return print_playlist_result(r, result); } CommandResult -handle_deleteid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_deleteid(Client &client, Request args, Response &r) { unsigned id; - - if (!check_unsigned(client, &id, argv[1])) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.DeleteId(id); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_playlist(Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_playlist(Client &client, gcc_unused Request args, Response &r) { - playlist_print_uris(client, client.playlist); + playlist_print_uris(r, client.partition, client.playlist); return CommandResult::OK; } CommandResult -handle_shuffle(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_shuffle(gcc_unused Client &client, Request args, Response &r) { - unsigned start = 0, end = client.playlist.queue.GetLength(); - if (argc == 2 && !check_range(client, &start, &end, argv[1])) + RangeArg range = RangeArg::All(); + if (!args.ParseOptional(0, range, r)) return CommandResult::ERROR; - client.partition.Shuffle(start, end); + client.partition.Shuffle(range.start, range.end); return CommandResult::OK; } CommandResult -handle_clear(gcc_unused Client &client, - gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_clear(Client &client, gcc_unused Request args, gcc_unused Response &r) { client.partition.ClearQueue(); return CommandResult::OK; } CommandResult -handle_plchanges(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_plchanges(Client &client, Request args, Response &r) { uint32_t version; - - if (!check_uint32(client, &version, argv[1])) + if (!ParseCommandArg32(r, version, args.front())) return CommandResult::ERROR; - playlist_print_changes_info(client, client.playlist, version); + playlist_print_changes_info(r, client.partition, + client.playlist, version); return CommandResult::OK; } CommandResult -handle_plchangesposid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_plchangesposid(Client &client, Request args, Response &r) { uint32_t version; - - if (!check_uint32(client, &version, argv[1])) + if (!ParseCommandArg32(r, version, args.front())) return CommandResult::ERROR; - playlist_print_changes_position(client, client.playlist, version); + playlist_print_changes_position(r, client.playlist, version); return CommandResult::OK; } CommandResult -handle_playlistinfo(Client &client, unsigned argc, char *argv[]) +handle_playlistinfo(Client &client, Request args, Response &r) { - unsigned start = 0, end = std::numeric_limits<unsigned>::max(); - bool ret; - - if (argc == 2 && !check_range(client, &start, &end, argv[1])) + RangeArg range = RangeArg::All(); + if (!args.ParseOptional(0, range, r)) return CommandResult::ERROR; - ret = playlist_print_info(client, client.playlist, start, end); - if (!ret) - return print_playlist_result(client, + if (!playlist_print_info(r, client.partition, client.playlist, + range.start, range.end)) + return print_playlist_result(r, PlaylistResult::BAD_RANGE); return CommandResult::OK; } CommandResult -handle_playlistid(Client &client, unsigned argc, char *argv[]) +handle_playlistid(Client &client, Request args, Response &r) { - if (argc >= 2) { + if (!args.IsEmpty()) { unsigned id; - if (!check_unsigned(client, &id, argv[1])) + if (!args.Parse(0, id, r)) return CommandResult::ERROR; - bool ret = playlist_print_id(client, client.playlist, id); + bool ret = playlist_print_id(r, client.partition, + client.playlist, id); if (!ret) - return print_playlist_result(client, - PlaylistResult::NO_SUCH_SONG); + return print_playlist_result(r, PlaylistResult::NO_SUCH_SONG); } else { - playlist_print_info(client, client.playlist, + playlist_print_info(r, client.partition, client.playlist, 0, std::numeric_limits<unsigned>::max()); } @@ -295,147 +303,120 @@ handle_playlistid(Client &client, unsigned argc, char *argv[]) } static CommandResult -handle_playlist_match(Client &client, unsigned argc, char *argv[], +handle_playlist_match(Client &client, Request args, Response &r, bool fold_case) { - ConstBuffer<const char *> args(argv + 1, argc - 1); - SongFilter filter; if (!filter.Parse(args, fold_case)) { - command_error(client, ACK_ERROR_ARG, "incorrect arguments"); + r.Error(ACK_ERROR_ARG, "incorrect arguments"); return CommandResult::ERROR; } - playlist_print_find(client, client.playlist, filter); + playlist_print_find(r, client.partition, client.playlist, filter); return CommandResult::OK; } CommandResult -handle_playlistfind(Client &client, unsigned argc, char *argv[]) +handle_playlistfind(Client &client, Request args, Response &r) { - return handle_playlist_match(client, argc, argv, false); + return handle_playlist_match(client, args, r, false); } CommandResult -handle_playlistsearch(Client &client, unsigned argc, char *argv[]) +handle_playlistsearch(Client &client, Request args, Response &r) { - return handle_playlist_match(client, argc, argv, true); + return handle_playlist_match(client, args, r, true); } CommandResult -handle_prio(Client &client, unsigned argc, char *argv[]) +handle_prio(Client &client, Request args, Response &r) { unsigned priority; - - if (!check_unsigned(client, &priority, argv[1])) + if (!args.ParseShift(0, priority, r, 0xff)) return CommandResult::ERROR; - if (priority > 0xff) { - command_error(client, ACK_ERROR_ARG, - "Priority out of range: %s", argv[1]); - return CommandResult::ERROR; - } - - for (unsigned i = 2; i < argc; ++i) { - unsigned start_position, end_position; - if (!check_range(client, &start_position, &end_position, - argv[i])) + for (const char *i : args) { + RangeArg range; + if (!ParseCommandArg(r, range, i)) return CommandResult::ERROR; PlaylistResult result = - client.partition.SetPriorityRange(start_position, - end_position, - priority); + client.partition.SetPriorityRange(range.start, + range.end, + priority); if (result != PlaylistResult::SUCCESS) - return print_playlist_result(client, result); + return print_playlist_result(r, result); } return CommandResult::OK; } CommandResult -handle_prioid(Client &client, unsigned argc, char *argv[]) +handle_prioid(Client &client, Request args, Response &r) { unsigned priority; - - if (!check_unsigned(client, &priority, argv[1])) + if (!args.ParseShift(0, priority, r, 0xff)) return CommandResult::ERROR; - if (priority > 0xff) { - command_error(client, ACK_ERROR_ARG, - "Priority out of range: %s", argv[1]); - return CommandResult::ERROR; - } - - for (unsigned i = 2; i < argc; ++i) { + for (const char *i : args) { unsigned song_id; - if (!check_unsigned(client, &song_id, argv[i])) + if (!ParseCommandArg(r, song_id, i)) return CommandResult::ERROR; PlaylistResult result = client.partition.SetPriorityId(song_id, priority); if (result != PlaylistResult::SUCCESS) - return print_playlist_result(client, result); + return print_playlist_result(r, result); } return CommandResult::OK; } CommandResult -handle_move(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_move(Client &client, Request args, Response &r) { - unsigned start, end; + RangeArg range; int to; - if (!check_range(client, &start, &end, argv[1])) - return CommandResult::ERROR; - if (!check_int(client, &to, argv[2])) + if (!args.Parse(0, range, r) || !args.Parse(1, to, r)) return CommandResult::ERROR; PlaylistResult result = - client.partition.MoveRange(start, end, to); - return print_playlist_result(client, result); + client.partition.MoveRange(range.start, range.end, to); + return print_playlist_result(r, result); } CommandResult -handle_moveid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_moveid(Client &client, Request args, Response &r) { unsigned id; int to; - - if (!check_unsigned(client, &id, argv[1])) - return CommandResult::ERROR; - if (!check_int(client, &to, argv[2])) + if (!args.Parse(0, id, r) || !args.Parse(1, to, r)) return CommandResult::ERROR; + PlaylistResult result = client.partition.MoveId(id, to); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_swap(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_swap(Client &client, Request args, Response &r) { unsigned song1, song2; - - if (!check_unsigned(client, &song1, argv[1])) - return CommandResult::ERROR; - if (!check_unsigned(client, &song2, argv[2])) + if (!args.Parse(0, song1, r) || !args.Parse(1, song2, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SwapPositions(song1, song2); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } CommandResult -handle_swapid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_swapid(Client &client, Request args, Response &r) { unsigned id1, id2; - - if (!check_unsigned(client, &id1, argv[1])) - return CommandResult::ERROR; - if (!check_unsigned(client, &id2, argv[2])) + if (!args.Parse(0, id1, r) || !args.Parse(1, id2, r)) return CommandResult::ERROR; PlaylistResult result = client.partition.SwapIds(id1, id2); - return print_playlist_result(client, result); + return print_playlist_result(r, result); } diff --git a/src/command/QueueCommands.hxx b/src/command/QueueCommands.hxx index f98f7bad2..49499d8ea 100644 --- a/src/command/QueueCommands.hxx +++ b/src/command/QueueCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,65 +23,67 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_add(Client &client, unsigned argc, char *argv[]); +handle_add(Client &client, Request request, Response &response); CommandResult -handle_addid(Client &client, unsigned argc, char *argv[]); +handle_addid(Client &client, Request request, Response &response); CommandResult -handle_rangeid(Client &client, unsigned argc, char *argv[]); +handle_rangeid(Client &client, Request request, Response &response); CommandResult -handle_delete(Client &client, unsigned argc, char *argv[]); +handle_delete(Client &client, Request request, Response &response); CommandResult -handle_deleteid(Client &client, unsigned argc, char *argv[]); +handle_deleteid(Client &client, Request request, Response &response); CommandResult -handle_playlist(Client &client, unsigned argc, char *argv[]); +handle_playlist(Client &client, Request request, Response &response); CommandResult -handle_shuffle(Client &client, unsigned argc, char *argv[]); +handle_shuffle(Client &client, Request request, Response &response); CommandResult -handle_clear(Client &client, unsigned argc, char *argv[]); +handle_clear(Client &client, Request request, Response &response); CommandResult -handle_plchanges(Client &client, unsigned argc, char *argv[]); +handle_plchanges(Client &client, Request request, Response &response); CommandResult -handle_plchangesposid(Client &client, unsigned argc, char *argv[]); +handle_plchangesposid(Client &client, Request request, Response &response); CommandResult -handle_playlistinfo(Client &client, unsigned argc, char *argv[]); +handle_playlistinfo(Client &client, Request request, Response &response); CommandResult -handle_playlistid(Client &client, unsigned argc, char *argv[]); +handle_playlistid(Client &client, Request request, Response &response); CommandResult -handle_playlistfind(Client &client, unsigned argc, char *argv[]); +handle_playlistfind(Client &client, Request request, Response &response); CommandResult -handle_playlistsearch(Client &client, unsigned argc, char *argv[]); +handle_playlistsearch(Client &client, Request request, Response &response); CommandResult -handle_prio(Client &client, unsigned argc, char *argv[]); +handle_prio(Client &client, Request request, Response &response); CommandResult -handle_prioid(Client &client, unsigned argc, char *argv[]); +handle_prioid(Client &client, Request request, Response &response); CommandResult -handle_move(Client &client, unsigned argc, char *argv[]); +handle_move(Client &client, Request request, Response &response); CommandResult -handle_moveid(Client &client, unsigned argc, char *argv[]); +handle_moveid(Client &client, Request request, Response &response); CommandResult -handle_swap(Client &client, unsigned argc, char *argv[]); +handle_swap(Client &client, Request request, Response &response); CommandResult -handle_swapid(Client &client, unsigned argc, char *argv[]); +handle_swapid(Client &client, Request request, Response &response); #endif diff --git a/src/command/Request.hxx b/src/command/Request.hxx new file mode 100644 index 000000000..1616b7045 --- /dev/null +++ b/src/command/Request.hxx @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2003-2015 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_REQUEST_HXX +#define MPD_REQUEST_HXX + +#include "check.h" +#include "protocol/ArgParser.hxx" +#include "util/ConstBuffer.hxx" + +#include <utility> + +#include <assert.h> + +class Response; + +class Request : public ConstBuffer<const char *> { + typedef ConstBuffer<const char *> Base; + +public: + constexpr Request(const char *const*argv, size_type n) + :Base(argv, n) {} + + constexpr const char *GetOptional(unsigned idx, + const char *default_value=nullptr) const { + return idx < size + ? data[idx] + : default_value; + } + + template<typename T, typename... Args> + bool Parse(unsigned idx, T &value_r, Response &r, + Args&&... args) { + assert(idx < size); + + return ParseCommandArg(r, value_r, data[idx], + std::forward<Args>(args)...); + } + + template<typename T, typename... Args> + bool ParseOptional(unsigned idx, T &value_r, Response &r, + Args&&... args) { + return idx >= size || + Parse(idx, value_r, r, + std::forward<Args>(args)...); + } + + template<typename T, typename... Args> + bool ParseShift(unsigned idx, T &value_r, Response &r, + Args&&... args) { + bool success = Parse(idx, value_r, r, + std::forward<Args>(args)...); + shift(); + return success; + } +}; + +#endif diff --git a/src/command/StickerCommands.cxx b/src/command/StickerCommands.cxx index 37506d51b..d5d7ab1f8 100644 --- a/src/command/StickerCommands.cxx +++ b/src/command/StickerCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "StickerCommands.hxx" +#include "Request.hxx" #include "SongPrint.hxx" #include "db/Interface.hxx" #include "db/DatabaseGlue.hxx" @@ -26,16 +27,17 @@ #include "sticker/StickerPrint.hxx" #include "sticker/StickerDatabase.hxx" #include "CommandError.hxx" -#include "protocol/Result.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "util/Error.hxx" - -#include <string.h> +#include "util/ConstBuffer.hxx" +#include "util/StringAPI.hxx" struct sticker_song_find_data { - Client &client; + Response &r; + Partition &partition; const char *name; }; @@ -46,125 +48,161 @@ sticker_song_find_print_cb(const LightSong &song, const char *value, struct sticker_song_find_data *data = (struct sticker_song_find_data *)user_data; - song_print_uri(data->client, song); - sticker_print_value(data->client, data->name, value); + song_print_uri(data->r, data->partition, song); + sticker_print_value(data->r, data->name, value); } static CommandResult -handle_sticker_song(Client &client, unsigned argc, char *argv[]) +handle_sticker_song(Response &r, Partition &partition, Request args) { Error error; - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) - return print_error(client, error); + return print_error(r, error); + + const char *const cmd = args.front(); /* get song song_id key */ - if (argc == 5 && strcmp(argv[1], "get") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + if (args.size == 4 && StringIsEqual(cmd, "get")) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); - const auto value = sticker_song_get_value(*song, argv[4]); + const auto value = sticker_song_get_value(*song, args[3], + error); db->ReturnSong(song); if (value.empty()) { - command_error(client, ACK_ERROR_NO_EXIST, - "no such sticker"); + if (error.IsDefined()) + return print_error(r, error); + + r.Error(ACK_ERROR_NO_EXIST, "no such sticker"); return CommandResult::ERROR; } - sticker_print_value(client, argv[4], value.c_str()); + sticker_print_value(r, args[3], value.c_str()); return CommandResult::OK; /* list song song_id */ - } else if (argc == 4 && strcmp(argv[1], "list") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + } else if (args.size == 3 && StringIsEqual(cmd, "list")) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); - sticker *sticker = sticker_song_get(*song); + Sticker *sticker = sticker_song_get(*song, error); db->ReturnSong(song); if (sticker) { - sticker_print(client, *sticker); + sticker_print(r, *sticker); sticker_free(sticker); - } + } else if (error.IsDefined()) + return print_error(r, error); return CommandResult::OK; /* set song song_id id key */ - } else if (argc == 6 && strcmp(argv[1], "set") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + } else if (args.size == 5 && StringIsEqual(cmd, "set")) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); - bool ret = sticker_song_set_value(*song, argv[4], argv[5]); + bool ret = sticker_song_set_value(*song, args[3], args[4], + error); db->ReturnSong(song); if (!ret) { - command_error(client, ACK_ERROR_SYSTEM, - "failed to set sticker value"); + if (error.IsDefined()) + return print_error(r, error); + + r.Error(ACK_ERROR_SYSTEM, + "failed to set sticker value"); return CommandResult::ERROR; } return CommandResult::OK; /* delete song song_id [key] */ - } else if ((argc == 4 || argc == 5) && - strcmp(argv[1], "delete") == 0) { - const LightSong *song = db->GetSong(argv[3], error); + } else if ((args.size == 3 || args.size == 4) && + StringIsEqual(cmd, "delete")) { + const LightSong *song = db->GetSong(args[2], error); if (song == nullptr) - return print_error(client, error); + return print_error(r, error); - bool ret = argc == 4 - ? sticker_song_delete(*song) - : sticker_song_delete_value(*song, argv[4]); + bool ret = args.size == 3 + ? sticker_song_delete(*song, error) + : sticker_song_delete_value(*song, args[3], error); db->ReturnSong(song); if (!ret) { - command_error(client, ACK_ERROR_SYSTEM, - "no such sticker"); + if (error.IsDefined()) + return print_error(r, error); + + r.Error(ACK_ERROR_SYSTEM, "no such sticker"); return CommandResult::ERROR; } return CommandResult::OK; /* find song dir key */ - } else if (argc == 5 && strcmp(argv[1], "find") == 0) { + } else if ((args.size == 4 || args.size == 6) && + StringIsEqual(cmd, "find")) { /* "sticker find song a/directory name" */ - const char *const base_uri = argv[3]; + const char *const base_uri = args[2]; + + StickerOperator op = StickerOperator::EXISTS; + const char *value = nullptr; + + if (args.size == 6) { + /* match the value */ + + const char *op_s = args[4]; + value = args[5]; + + if (StringIsEqual(op_s, "=")) + op = StickerOperator::EQUALS; + else if (StringIsEqual(op_s, "<")) + op = StickerOperator::LESS_THAN; + else if (StringIsEqual(op_s, ">")) + op = StickerOperator::GREATER_THAN; + else { + r.Error(ACK_ERROR_ARG, "bad operator"); + return CommandResult::ERROR; + } + } - bool success; struct sticker_song_find_data data = { - client, - argv[4], + r, + partition, + args[3], }; - success = sticker_song_find(*db, base_uri, data.name, - sticker_song_find_print_cb, &data); - if (!success) { - command_error(client, ACK_ERROR_SYSTEM, - "failed to set search sticker database"); + if (!sticker_song_find(*db, base_uri, data.name, + op, value, + sticker_song_find_print_cb, &data, + error)) { + if (error.IsDefined()) + return print_error(r, error); + + r.Error(ACK_ERROR_SYSTEM, + "failed to set search sticker database"); return CommandResult::ERROR; } return CommandResult::OK; } else { - command_error(client, ACK_ERROR_ARG, "bad request"); + r.Error(ACK_ERROR_ARG, "bad request"); return CommandResult::ERROR; } } CommandResult -handle_sticker(Client &client, unsigned argc, char *argv[]) +handle_sticker(Client &client, Request args, Response &r) { - assert(argc >= 4); + assert(args.size >= 3); if (!sticker_enabled()) { - command_error(client, ACK_ERROR_UNKNOWN, - "sticker database is disabled"); + r.Error(ACK_ERROR_UNKNOWN, "sticker database is disabled"); return CommandResult::ERROR; } - if (strcmp(argv[2], "song") == 0) - return handle_sticker_song(client, argc, argv); + if (StringIsEqual(args[1], "song")) + return handle_sticker_song(r, client.partition, args); else { - command_error(client, ACK_ERROR_ARG, - "unknown sticker domain"); + r.Error(ACK_ERROR_ARG, "unknown sticker domain"); return CommandResult::ERROR; } } diff --git a/src/command/StickerCommands.hxx b/src/command/StickerCommands.hxx index cf46cd034..5bb9cc426 100644 --- a/src/command/StickerCommands.hxx +++ b/src/command/StickerCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,8 +23,10 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_sticker(Client &client, unsigned argc, char *argv[]); +handle_sticker(Client &client, Request request, Response &response); #endif diff --git a/src/command/StorageCommands.cxx b/src/command/StorageCommands.cxx index ee51c573e..3c11eb0d7 100644 --- a/src/command/StorageCommands.cxx +++ b/src/command/StorageCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,12 +21,14 @@ #include "config.h" #include "StorageCommands.hxx" +#include "Request.hxx" #include "CommandError.hxx" -#include "protocol/Result.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" +#include "util/ConstBuffer.hxx" #include "fs/Traits.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" #include "Partition.hxx" #include "Instance.hxx" #include "storage/Registry.hxx" @@ -55,7 +57,7 @@ skip_path(const char *name_utf8) #endif static bool -handle_listfiles_storage(Client &client, StorageDirectoryReader &reader, +handle_listfiles_storage(Response &r, StorageDirectoryReader &reader, Error &error) { const char *name_utf8; @@ -63,29 +65,29 @@ handle_listfiles_storage(Client &client, StorageDirectoryReader &reader, if (skip_path(name_utf8)) continue; - FileInfo info; + StorageFileInfo info; if (!reader.GetInfo(false, info, error)) continue; switch (info.type) { - case FileInfo::Type::OTHER: + case StorageFileInfo::Type::OTHER: /* ignore */ continue; - case FileInfo::Type::REGULAR: - client_printf(client, "file: %s\n" - "size: %" PRIu64 "\n", - name_utf8, - info.size); + case StorageFileInfo::Type::REGULAR: + r.Format("file: %s\n" + "size: %" PRIu64 "\n", + name_utf8, + info.size); break; - case FileInfo::Type::DIRECTORY: - client_printf(client, "directory: %s\n", name_utf8); + case StorageFileInfo::Type::DIRECTORY: + r.Format("directory: %s\n", name_utf8); break; } if (info.mtime != 0) - time_print(client, "Last-Modified", info.mtime); + time_print(r, "Last-Modified", info.mtime); } return true; @@ -96,58 +98,57 @@ handle_listfiles_storage(Client &client, StorageDirectoryReader &reader, #endif static bool -handle_listfiles_storage(Client &client, Storage &storage, const char *uri, +handle_listfiles_storage(Response &r, Storage &storage, const char *uri, Error &error) { auto reader = storage.OpenDirectory(uri, error); if (reader == nullptr) return false; - bool success = handle_listfiles_storage(client, *reader, error); + bool success = handle_listfiles_storage(r, *reader, error); delete reader; return success; } CommandResult -handle_listfiles_storage(Client &client, Storage &storage, const char *uri) +handle_listfiles_storage(Response &r, Storage &storage, const char *uri) { Error error; - if (!handle_listfiles_storage(client, storage, uri, error)) - return print_error(client, error); + if (!handle_listfiles_storage(r, storage, uri, error)) + return print_error(r, error); return CommandResult::OK; } CommandResult -handle_listfiles_storage(Client &client, const char *uri) +handle_listfiles_storage(Response &r, const char *uri) { Error error; Storage *storage = CreateStorageURI(io_thread_get(), uri, error); if (storage == nullptr) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_ARG, - "Unrecognized storage URI"); + r.Error(ACK_ERROR_ARG, "Unrecognized storage URI"); return CommandResult::ERROR; } - bool success = handle_listfiles_storage(client, *storage, "", error); + bool success = handle_listfiles_storage(r, *storage, "", error); delete storage; if (!success) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } static void -print_storage_uri(Client &client, const Storage &storage) +print_storage_uri(Client &client, Response &r, const Storage &storage) { std::string uri = storage.MapUTF8(""); if (uri.empty()) return; - if (PathTraitsFS::IsAbsolute(uri.c_str())) { + if (PathTraitsUTF8::IsAbsolute(uri.c_str())) { /* storage points to local directory */ if (!client.IsLocal()) @@ -163,24 +164,24 @@ print_storage_uri(Client &client, const Storage &storage) uri = std::move(allocated); } - client_printf(client, "storage: %s\n", uri.c_str()); + r.Format("storage: %s\n", uri.c_str()); } CommandResult -handle_listmounts(Client &client, gcc_unused unsigned argc, gcc_unused char *argv[]) +handle_listmounts(Client &client, gcc_unused Request args, Response &r) { Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } CompositeStorage &composite = *(CompositeStorage *)_composite; - const auto visitor = [&client](const char *mount_uri, - const Storage &storage){ - client_printf(client, "mount: %s\n", mount_uri); - print_storage_uri(client, storage); + const auto visitor = [&client, &r](const char *mount_uri, + const Storage &storage){ + r.Format("mount: %s\n", mount_uri); + print_storage_uri(client, r, storage); }; composite.VisitMounts(visitor); @@ -189,21 +190,21 @@ handle_listmounts(Client &client, gcc_unused unsigned argc, gcc_unused char *arg } CommandResult -handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_mount(Client &client, Request args, Response &r) { Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } CompositeStorage &composite = *(CompositeStorage *)_composite; - const char *const local_uri = argv[1]; - const char *const remote_uri = argv[2]; + const char *const local_uri = args[0]; + const char *const remote_uri = args[1]; if (*local_uri == 0) { - command_error(client, ACK_ERROR_ARG, "Bad mount point"); + r.Error(ACK_ERROR_ARG, "Bad mount point"); return CommandResult::ERROR; } @@ -213,7 +214,7 @@ handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) UpdateQueue::Erase() really gets called for every unmount, and no Directory disappears recursively during database update */ - command_error(client, ACK_ERROR_ARG, "Bad mount point"); + r.Error(ACK_ERROR_ARG, "Bad mount point"); return CommandResult::ERROR; } @@ -222,10 +223,9 @@ handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) error); if (storage == nullptr) { if (error.IsDefined()) - return print_error(client, error); + return print_error(r, error); - command_error(client, ACK_ERROR_ARG, - "Unrecognized storage URI"); + r.Error(ACK_ERROR_ARG, "Unrecognized storage URI"); return CommandResult::ERROR; } @@ -239,7 +239,7 @@ handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) if (!db.Mount(local_uri, remote_uri, error)) { composite.Unmount(local_uri); - return print_error(client, error); + return print_error(r, error); } // TODO: call Instance::OnDatabaseModified()? @@ -252,20 +252,20 @@ handle_mount(Client &client, gcc_unused unsigned argc, char *argv[]) } CommandResult -handle_unmount(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_unmount(Client &client, Request args, Response &r) { Storage *_composite = client.partition.instance.storage; if (_composite == nullptr) { - command_error(client, ACK_ERROR_NO_EXIST, "No database"); + r.Error(ACK_ERROR_NO_EXIST, "No database"); return CommandResult::ERROR; } CompositeStorage &composite = *(CompositeStorage *)_composite; - const char *const local_uri = argv[1]; + const char *const local_uri = args.front(); if (*local_uri == 0) { - command_error(client, ACK_ERROR_ARG, "Bad mount point"); + r.Error(ACK_ERROR_ARG, "Bad mount point"); return CommandResult::ERROR; } @@ -287,7 +287,7 @@ handle_unmount(Client &client, gcc_unused unsigned argc, char *argv[]) #endif if (!composite.Unmount(local_uri)) { - command_error(client, ACK_ERROR_ARG, "Not a mount point"); + r.Error(ACK_ERROR_ARG, "Not a mount point"); return CommandResult::ERROR; } diff --git a/src/command/StorageCommands.hxx b/src/command/StorageCommands.hxx index a3636d54a..7d3c552f6 100644 --- a/src/command/StorageCommands.hxx +++ b/src/command/StorageCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,20 +24,22 @@ class Client; class Storage; +class Request; +class Response; CommandResult -handle_listfiles_storage(Client &client, Storage &storage, const char *uri); +handle_listfiles_storage(Response &r, Storage &storage, const char *uri); CommandResult -handle_listfiles_storage(Client &client, const char *uri); +handle_listfiles_storage(Response &r, const char *uri); CommandResult -handle_listmounts(Client &client, unsigned argc, char *argv[]); +handle_listmounts(Client &client, Request request, Response &response); CommandResult -handle_mount(Client &client, unsigned argc, char *argv[]); +handle_mount(Client &client, Request request, Response &response); CommandResult -handle_unmount(Client &client, unsigned argc, char *argv[]); +handle_unmount(Client &client, Request request, Response &response); #endif diff --git a/src/command/TagCommands.cxx b/src/command/TagCommands.cxx index 2d537671c..2a7076bdc 100644 --- a/src/command/TagCommands.cxx +++ b/src/command/TagCommands.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,51 +19,51 @@ #include "config.h" #include "TagCommands.hxx" +#include "Request.hxx" #include "CommandError.hxx" #include "client/Client.hxx" -#include "protocol/ArgParser.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "tag/Tag.hxx" #include "Partition.hxx" +#include "util/ConstBuffer.hxx" CommandResult -handle_addtagid(Client &client, gcc_unused unsigned argc, char *argv[]) +handle_addtagid(Client &client, Request args, Response &r) { unsigned song_id; - if (!check_unsigned(client, &song_id, argv[1])) + if (!args.Parse(0, song_id, r)) return CommandResult::ERROR; - const char *const tag_name = argv[2]; + const char *const tag_name = args[1]; const TagType tag_type = tag_name_parse_i(tag_name); if (tag_type == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, - "Unknown tag type: %s", tag_name); + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name); return CommandResult::ERROR; } - const char *const value = argv[3]; + const char *const value = args[2]; Error error; if (!client.partition.playlist.AddSongIdTag(song_id, tag_type, value, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } CommandResult -handle_cleartagid(Client &client, unsigned argc, char *argv[]) +handle_cleartagid(Client &client, Request args, Response &r) { unsigned song_id; - if (!check_unsigned(client, &song_id, argv[1])) + if (!args.Parse(0, song_id, r)) return CommandResult::ERROR; TagType tag_type = TAG_NUM_OF_ITEM_TYPES; - if (argc >= 3) { - const char *const tag_name = argv[2]; + if (args.size >= 2) { + const char *const tag_name = args[1]; tag_type = tag_name_parse_i(tag_name); if (tag_type == TAG_NUM_OF_ITEM_TYPES) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Unknown tag type: %s", tag_name); return CommandResult::ERROR; } @@ -72,7 +72,7 @@ handle_cleartagid(Client &client, unsigned argc, char *argv[]) Error error; if (!client.partition.playlist.ClearSongIdTag(song_id, tag_type, error)) - return print_error(client, error); + return print_error(r, error); return CommandResult::OK; } diff --git a/src/command/TagCommands.hxx b/src/command/TagCommands.hxx index 748838e68..868d6d783 100644 --- a/src/command/TagCommands.hxx +++ b/src/command/TagCommands.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,11 +23,13 @@ #include "CommandResult.hxx" class Client; +class Request; +class Response; CommandResult -handle_addtagid(Client &client, unsigned argc, char *argv[]); +handle_addtagid(Client &client, Request request, Response &response); CommandResult -handle_cleartagid(Client &client, unsigned argc, char *argv[]); +handle_cleartagid(Client &client, Request request, Response &response); #endif diff --git a/src/config/ConfigData.cxx b/src/config/Block.cxx index 70e1e55ed..a74903b10 100644 --- a/src/config/ConfigData.cxx +++ b/src/config/Block.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,18 +18,18 @@ */ #include "config.h" -#include "ConfigData.hxx" +#include "Block.hxx" #include "ConfigParser.hxx" #include "ConfigPath.hxx" -#include "util/Error.hxx" -#include "fs/AllocatedPath.hxx" #include "system/FatalError.hxx" +#include "fs/AllocatedPath.hxx" +#include "util/Error.hxx" #include <assert.h> #include <stdlib.h> int -block_param::GetIntValue() const +BlockParam::GetIntValue() const { char *endptr; long value2 = strtol(value.c_str(), &endptr, 0); @@ -40,7 +40,7 @@ block_param::GetIntValue() const } unsigned -block_param::GetUnsignedValue() const +BlockParam::GetUnsignedValue() const { char *endptr; unsigned long value2 = strtoul(value.c_str(), &endptr, 0); @@ -51,7 +51,7 @@ block_param::GetUnsignedValue() const } bool -block_param::GetBoolValue() const +BlockParam::GetBoolValue() const { bool value2; if (!get_bool(value.c_str(), &value2)) @@ -62,16 +62,13 @@ block_param::GetBoolValue() const return value2; } -config_param::config_param(const char *_value, int _line) - :next(nullptr), value(_value), line(_line), used(false) {} - -config_param::~config_param() +ConfigBlock::~ConfigBlock() { delete next; } -const block_param * -config_param::GetBlockParam(const char *name) const +const BlockParam * +ConfigBlock::GetBlockParam(const char *name) const { for (const auto &i : block_params) { if (i.name == name) { @@ -80,13 +77,13 @@ config_param::GetBlockParam(const char *name) const } } - return NULL; + return nullptr; } const char * -config_param::GetBlockValue(const char *name, const char *default_value) const +ConfigBlock::GetBlockValue(const char *name, const char *default_value) const { - const block_param *bp = GetBlockParam(name); + const BlockParam *bp = GetBlockParam(name); if (bp == nullptr) return default_value; @@ -94,7 +91,7 @@ config_param::GetBlockValue(const char *name, const char *default_value) const } AllocatedPath -config_param::GetBlockPath(const char *name, const char *default_value, +ConfigBlock::GetBlockPath(const char *name, const char *default_value, Error &error) const { assert(!error.IsDefined()); @@ -102,7 +99,7 @@ config_param::GetBlockPath(const char *name, const char *default_value, int line2 = line; const char *s; - const block_param *bp = GetBlockParam(name); + const BlockParam *bp = GetBlockParam(name); if (bp != nullptr) { line2 = bp->line; s = bp->value.c_str(); @@ -122,15 +119,15 @@ config_param::GetBlockPath(const char *name, const char *default_value, } AllocatedPath -config_param::GetBlockPath(const char *name, Error &error) const +ConfigBlock::GetBlockPath(const char *name, Error &error) const { return GetBlockPath(name, nullptr, error); } int -config_param::GetBlockValue(const char *name, int default_value) const +ConfigBlock::GetBlockValue(const char *name, int default_value) const { - const block_param *bp = GetBlockParam(name); + const BlockParam *bp = GetBlockParam(name); if (bp == nullptr) return default_value; @@ -138,9 +135,9 @@ config_param::GetBlockValue(const char *name, int default_value) const } unsigned -config_param::GetBlockValue(const char *name, unsigned default_value) const +ConfigBlock::GetBlockValue(const char *name, unsigned default_value) const { - const block_param *bp = GetBlockParam(name); + const BlockParam *bp = GetBlockParam(name); if (bp == nullptr) return default_value; @@ -149,10 +146,10 @@ config_param::GetBlockValue(const char *name, unsigned default_value) const gcc_pure bool -config_param::GetBlockValue(const char *name, bool default_value) const +ConfigBlock::GetBlockValue(const char *name, bool default_value) const { - const block_param *bp = GetBlockParam(name); - if (bp == NULL) + const BlockParam *bp = GetBlockParam(name); + if (bp == nullptr) return default_value; return bp->GetBoolValue(); diff --git a/src/config/ConfigData.hxx b/src/config/Block.hxx index e42d674ba..9e72018ca 100644 --- a/src/config/ConfigData.hxx +++ b/src/config/Block.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,20 +17,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_CONFIG_DATA_HXX -#define MPD_CONFIG_DATA_HXX +#ifndef MPD_CONFIG_BLOCK_HXX +#define MPD_CONFIG_BLOCK_HXX -#include "ConfigOption.hxx" +#include "check.h" +#include "Param.hxx" #include "Compiler.h" #include <string> -#include <array> #include <vector> -class AllocatedPath; class Error; +class AllocatedPath; -struct block_param { +struct BlockParam { std::string name; std::string value; int line; @@ -42,7 +42,7 @@ struct block_param { mutable bool used; gcc_nonnull_all - block_param(const char *_name, const char *_value, int _line=-1) + BlockParam(const char *_name, const char *_value, int _line=-1) :name(_name), value(_value), line(_line), used(false) {} gcc_pure @@ -55,18 +55,16 @@ struct block_param { bool GetBoolValue() const; }; -struct config_param { +struct ConfigBlock { /** - * The next config_param with the same name. The destructor + * The next #ConfigBlock with the same name. The destructor * deletes the whole chain. */ - struct config_param *next; - - std::string value; + ConfigBlock *next; - unsigned int line; + int line; - std::vector<block_param> block_params; + std::vector<BlockParam> block_params; /** * This flag is false when nobody has queried the value of @@ -74,17 +72,14 @@ struct config_param { */ bool used; - config_param(int _line=-1) + explicit ConfigBlock(int _line=-1) :next(nullptr), line(_line), used(false) {} - gcc_nonnull_all - config_param(const char *_value, int _line=-1); - - config_param(const config_param &) = delete; + ConfigBlock(const ConfigBlock &) = delete; - ~config_param(); + ~ConfigBlock(); - config_param &operator=(const config_param &) = delete; + ConfigBlock &operator=(const ConfigBlock &) = delete; /** * Determine if this is a "null" instance, i.e. an empty @@ -92,7 +87,12 @@ struct config_param { * configuration file. */ bool IsNull() const { - return line == unsigned(-1); + return line < 0; + } + + gcc_pure + bool IsEmpty() const { + return block_params.empty(); } gcc_nonnull_all @@ -102,14 +102,14 @@ struct config_param { } gcc_nonnull_all gcc_pure - const block_param *GetBlockParam(const char *_name) const; + const BlockParam *GetBlockParam(const char *_name) const; gcc_pure const char *GetBlockValue(const char *name, const char *default_value=nullptr) const; /** - * Same as config_dup_path(), but looks up the setting in the + * Same as config_get_path(), but looks up the setting in the * specified block. */ AllocatedPath GetBlockPath(const char *name, const char *default_value, @@ -127,8 +127,4 @@ struct config_param { bool GetBlockValue(const char *name, bool default_value) const; }; -struct ConfigData { - std::array<config_param *, std::size_t(CONF_MAX)> params; -}; - #endif diff --git a/src/config/ConfigDefaults.hxx b/src/config/ConfigDefaults.hxx index c50f28c91..2dca5d469 100644 --- a/src/config/ConfigDefaults.hxx +++ b/src/config/ConfigDefaults.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/config/ConfigError.cxx b/src/config/ConfigError.cxx index 70aff7175..b2a773b15 100644 --- a/src/config/ConfigError.cxx +++ b/src/config/ConfigError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/config/ConfigError.hxx b/src/config/ConfigError.hxx index cbfa79df3..89543599d 100644 --- a/src/config/ConfigError.hxx +++ b/src/config/ConfigError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/config/ConfigFile.cxx b/src/config/ConfigFile.cxx index 1329c4cd4..b6164b8bc 100644 --- a/src/config/ConfigFile.cxx +++ b/src/config/ConfigFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,28 +19,27 @@ #include "config.h" #include "ConfigFile.hxx" -#include "ConfigData.hxx" +#include "Data.hxx" +#include "Param.hxx" +#include "Block.hxx" #include "ConfigTemplates.hxx" #include "util/Tokenizer.hxx" #include "util/StringUtil.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#include "fs/Limits.hxx" #include "fs/Path.hxx" -#include "fs/FileSystem.hxx" +#include "fs/io/FileReader.hxx" +#include "fs/io/BufferedReader.hxx" #include "Log.hxx" #include <assert.h> -#include <stdio.h> -#define MAX_STRING_SIZE MPD_PATH_MAX+80 - -#define CONF_COMMENT '#' +static constexpr char CONF_COMMENT = '#'; static constexpr Domain config_file_domain("config_file"); static bool -config_read_name_value(struct config_param *param, char *input, unsigned line, +config_read_name_value(ConfigBlock &block, char *input, unsigned line, Error &error) { Tokenizer tokenizer(input); @@ -67,7 +66,7 @@ config_read_name_value(struct config_param *param, char *input, unsigned line, return false; } - const struct block_param *bp = param->GetBlockParam(name); + const BlockParam *bp = block.GetBlockParam(name); if (bp != nullptr) { error.Format(config_file_domain, "\"%s\" is duplicate, first defined on line %i", @@ -75,27 +74,26 @@ config_read_name_value(struct config_param *param, char *input, unsigned line, return false; } - param->AddBlockParam(name, value, line); + block.AddBlockParam(name, value, line); return true; } -static struct config_param * -config_read_block(FILE *fp, int *count, char *string, Error &error) +static ConfigBlock * +config_read_block(BufferedReader &reader, Error &error) { - struct config_param *ret = new config_param(*count); + auto *ret = new ConfigBlock(reader.GetLineNumber()); while (true) { - char *line; - - line = fgets(string, MAX_STRING_SIZE, fp); + char *line = reader.ReadLine(); if (line == nullptr) { delete ret; - error.Set(config_file_domain, - "Expected '}' before end-of-file"); + + if (reader.Check(error)) + error.Set(config_file_domain, + "Expected '}' before end-of-file"); return nullptr; } - (*count)++; line = StripLeft(line); if (*line == 0 || *line == CONF_COMMENT) continue; @@ -108,8 +106,8 @@ config_read_block(FILE *fp, int *count, char *string, Error &error) if (*line != 0 && *line != CONF_COMMENT) { delete ret; error.Format(config_file_domain, - "line %i: Unknown tokens after '}'", - *count); + "line %y: Unknown tokens after '}'", + reader.GetLineNumber()); return nullptr; } @@ -118,10 +116,11 @@ config_read_block(FILE *fp, int *count, char *string, Error &error) /* parse name and value */ - if (!config_read_name_value(ret, line, *count, error)) { + if (!config_read_name_value(*ret, line, reader.GetLineNumber(), + error)) { assert(*line != 0); delete ret; - error.FormatPrefix("line %i: ", *count); + error.FormatPrefix("line %u: ", reader.GetLineNumber()); return nullptr; } } @@ -129,6 +128,64 @@ config_read_block(FILE *fp, int *count, char *string, Error &error) gcc_nonnull_all static void +Append(ConfigBlock *&head, ConfigBlock *p) +{ + assert(p->next == nullptr); + + auto **i = &head; + while (*i != nullptr) + i = &(*i)->next; + + *i = p; +} + +static bool +ReadConfigBlock(ConfigData &config_data, BufferedReader &reader, + const char *name, ConfigBlockOption o, + Tokenizer &tokenizer, + Error &error) +{ + const unsigned i = unsigned(o); + const ConfigTemplate &option = config_block_templates[i]; + ConfigBlock *&head = config_data.blocks[i]; + + if (head != nullptr && !option.repeatable) { + ConfigBlock *block = head; + error.Format(config_file_domain, + "config parameter \"%s\" is first defined " + "on line %d and redefined on line %u\n", + name, block->line, + reader.GetLineNumber()); + return false; + } + + /* now parse the block or the value */ + + if (tokenizer.CurrentChar() != '{') { + error.Format(config_file_domain, + "line %u: '{' expected", + reader.GetLineNumber()); + return false; + } + + char *line = StripLeft(tokenizer.Rest() + 1); + if (*line != 0 && *line != CONF_COMMENT) { + error.Format(config_file_domain, + "line %u: Unknown tokens after '{'", + reader.GetLineNumber()); + return false; + } + + auto *param = config_read_block(reader, error); + if (param == nullptr) + return false; + + Append(head, param); + return true; +} + +gcc_nonnull_all +static void Append(config_param *&head, config_param *p) { assert(p->next == nullptr); @@ -141,21 +198,62 @@ Append(config_param *&head, config_param *p) } static bool -ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error) +ReadConfigParam(ConfigData &config_data, BufferedReader &reader, + const char *name, ConfigOption o, + Tokenizer &tokenizer, + Error &error) { - assert(fp != nullptr); + const unsigned i = unsigned(o); + const ConfigTemplate &option = config_param_templates[i]; + config_param *&head = config_data.params[i]; - char string[MAX_STRING_SIZE + 1]; - int count = 0; - struct config_param *param; + if (head != nullptr && !option.repeatable) { + struct config_param *param = head; + error.Format(config_file_domain, + "config parameter \"%s\" is first defined " + "on line %d and redefined on line %u\n", + name, param->line, + reader.GetLineNumber()); + return false; + } - while (fgets(string, MAX_STRING_SIZE, fp)) { - char *line; - const char *name, *value; + /* now parse the block or the value */ - count++; + const char *value = tokenizer.NextString(error); + if (value == nullptr) { + if (tokenizer.IsEnd()) + error.Format(config_file_domain, + "line %u: Value missing", + reader.GetLineNumber()); + else + error.FormatPrefix("line %u: ", + reader.GetLineNumber()); - line = StripLeft(string); + return false; + } + + if (!tokenizer.IsEnd() && + tokenizer.CurrentChar() != CONF_COMMENT) { + error.Format(config_file_domain, + "line %u: Unknown tokens after value", + reader.GetLineNumber()); + return false; + } + + auto *param = new config_param(value, reader.GetLineNumber()); + Append(head, param); + return true; +} + +static bool +ReadConfigFile(ConfigData &config_data, BufferedReader &reader, Error &error) +{ + while (true) { + char *line = reader.ReadLine(); + if (line == nullptr) + return true; + + line = StripLeft(line); if (*line == 0 || *line == CONF_COMMENT) continue; @@ -163,10 +261,10 @@ ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error) by either the value or '{' */ Tokenizer tokenizer(line); - name = tokenizer.NextWord(error); + const char *name = tokenizer.NextWord(error); if (name == nullptr) { assert(!tokenizer.IsEnd()); - error.FormatPrefix("line %i: ", count); + error.FormatPrefix("line %u: ", reader.GetLineNumber()); return false; } @@ -174,79 +272,23 @@ ReadConfigFile(ConfigData &config_data, FILE *fp, Error &error) "repeatable" flag */ const ConfigOption o = ParseConfigOptionName(name); - if (o == CONF_MAX) { - error.Format(config_file_domain, - "unrecognized parameter in config file at " - "line %i: %s\n", count, name); - return false; - } - - const unsigned i = unsigned(o); - const ConfigTemplate &option = config_templates[i]; - config_param *&head = config_data.params[i]; - - if (head != nullptr && !option.repeatable) { - param = head; - error.Format(config_file_domain, - "config parameter \"%s\" is first defined " - "on line %i and redefined on line %i\n", - name, param->line, count); - return false; - } - - /* now parse the block or the value */ - - if (option.block) { - /* it's a block, call config_read_block() */ - - if (tokenizer.CurrentChar() != '{') { - error.Format(config_file_domain, - "line %i: '{' expected", count); - return false; - } - - line = StripLeft(tokenizer.Rest() + 1); - if (*line != 0 && *line != CONF_COMMENT) { - error.Format(config_file_domain, - "line %i: Unknown tokens after '{'", - count); + ConfigBlockOption bo; + if (o != ConfigOption::MAX) { + if (!ReadConfigParam(config_data, reader, name, o, + tokenizer, error)) return false; - } - - param = config_read_block(fp, &count, string, error); - if (param == nullptr) { + } else if ((bo = ParseConfigBlockOptionName(name)) != ConfigBlockOption::MAX) { + if (!ReadConfigBlock(config_data, reader, name, bo, + tokenizer, error)) return false; - } } else { - /* a string value */ - - value = tokenizer.NextString(error); - if (value == nullptr) { - if (tokenizer.IsEnd()) - error.Format(config_file_domain, - "line %i: Value missing", - count); - else - error.FormatPrefix("line %i: ", count); - - return false; - } - - if (!tokenizer.IsEnd() && - tokenizer.CurrentChar() != CONF_COMMENT) { - error.Format(config_file_domain, - "line %i: Unknown tokens after value", - count); - return false; - } - - param = new config_param(value, count); + error.Format(config_file_domain, + "unrecognized parameter in config file at " + "line %u: %s\n", + reader.GetLineNumber(), name); + return false; } - - Append(head, param); } - - return true; } bool @@ -257,13 +299,11 @@ ReadConfigFile(ConfigData &config_data, Path path, Error &error) FormatDebug(config_file_domain, "loading file %s", path_utf8.c_str()); - FILE *fp = FOpen(path, FOpenMode::ReadText); - if (fp == nullptr) { - error.FormatErrno("Failed to open %s", path_utf8.c_str()); + FileReader file(path, error); + if (!file.IsDefined()) return false; - } - bool result = ReadConfigFile(config_data, fp, error); - fclose(fp); - return result; + BufferedReader reader(file); + return ReadConfigFile(config_data, reader, error) && + reader.Check(error); } diff --git a/src/config/ConfigFile.hxx b/src/config/ConfigFile.hxx index b87182c6a..30bee0614 100644 --- a/src/config/ConfigFile.hxx +++ b/src/config/ConfigFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/config/ConfigGlobal.cxx b/src/config/ConfigGlobal.cxx index 9bc83398c..192baffec 100644 --- a/src/config/ConfigGlobal.cxx +++ b/src/config/ConfigGlobal.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,9 @@ #include "config.h" #include "ConfigGlobal.hxx" #include "ConfigParser.hxx" -#include "ConfigData.hxx" +#include "Data.hxx" +#include "Param.hxx" +#include "Block.hxx" #include "ConfigFile.hxx" #include "ConfigPath.hxx" #include "ConfigError.hxx" @@ -36,8 +38,7 @@ static ConfigData config_data; void config_global_finish(void) { - for (auto i : config_data.params) - delete i; + config_data.Clear(); } void config_global_init(void) @@ -51,15 +52,15 @@ ReadConfigFile(Path path, Error &error) } static void -Check(const config_param *param) +Check(const ConfigBlock &block) { - if (!param->used) - /* this whole config_param was not queried at all - + if (!block.used) + /* this whole block was not queried at all - the feature might be disabled at compile time? Silently ignore it here. */ return; - for (const auto &i : param->block_params) { + for (const auto &i : block.block_params) { if (!i.used) FormatWarning(config_domain, "option '%s' on line %i was not recognized", @@ -69,9 +70,9 @@ Check(const config_param *param) void config_global_check(void) { - for (auto i : config_data.params) - for (const config_param *p = i; p != nullptr; p = p->next) - Check(p); + for (auto i : config_data.blocks) + for (const auto *p = i; p != nullptr; p = p->next) + Check(*p); } const config_param * @@ -83,18 +84,27 @@ config_get_param(ConfigOption option) return param; } -const config_param * -config_find_block(ConfigOption option, const char *key, const char *value) +const ConfigBlock * +config_get_block(ConfigBlockOption option) +{ + ConfigBlock *block = config_data.blocks[unsigned(option)]; + if (block != nullptr) + block->used = true; + return block; +} + +const ConfigBlock * +config_find_block(ConfigBlockOption option, const char *key, const char *value) { - for (const config_param *param = config_get_param(option); - param != nullptr; param = param->next) { - const char *value2 = param->GetBlockValue(key); + for (const auto *block = config_get_block(option); + block != nullptr; block = block->next) { + const char *value2 = block->GetBlockValue(key); if (value2 == nullptr) FormatFatalError("block without '%s' name in line %d", - key, param->line); + key, block->line); if (strcmp(value2, value) == 0) - return param; + return block; } return nullptr; diff --git a/src/config/ConfigGlobal.hxx b/src/config/ConfigGlobal.hxx index 831418d03..b24b9aada 100644 --- a/src/config/ConfigGlobal.hxx +++ b/src/config/ConfigGlobal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,15 +27,20 @@ class Error; class Path; class AllocatedPath; struct config_param; +struct ConfigBlock; -void config_global_init(void); -void config_global_finish(void); +void +config_global_init(); + +void +config_global_finish(); /** * Call this function after all configuration has been evaluated. It * checks for unused parameters, and logs warnings. */ -void config_global_check(void); +void +config_global_check(); bool ReadConfigFile(Path path, Error &error); @@ -44,6 +49,10 @@ gcc_pure const config_param * config_get_param(enum ConfigOption option); +gcc_pure +const ConfigBlock * +config_get_block(enum ConfigBlockOption option); + /** * Find a block with a matching attribute. * @@ -52,8 +61,8 @@ config_get_param(enum ConfigOption option); * @param value the expected attribute value */ gcc_pure -const config_param * -config_find_block(ConfigOption option, const char *key, const char *value); +const ConfigBlock * +config_find_block(ConfigBlockOption option, const char *key, const char *value); /* Note on gcc_pure: Some of the functions declared pure are not really pure in strict sense. They have side effect such that they @@ -64,7 +73,7 @@ config_find_block(ConfigOption option, const char *key, const char *value); gcc_pure const char * -config_get_string(enum ConfigOption option, const char *default_value); +config_get_string(enum ConfigOption option, const char *default_value=nullptr); /** * Returns an optional configuration variable which contains an diff --git a/src/config/ConfigOption.hxx b/src/config/ConfigOption.hxx index 8eb4c7eaf..5acd6fd49 100644 --- a/src/config/ConfigOption.hxx +++ b/src/config/ConfigOption.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,71 +22,93 @@ #include "Compiler.h" -enum ConfigOption { - CONF_MUSIC_DIR, - CONF_PLAYLIST_DIR, - CONF_FOLLOW_INSIDE_SYMLINKS, - CONF_FOLLOW_OUTSIDE_SYMLINKS, - CONF_DB_FILE, - CONF_STICKER_FILE, - CONF_LOG_FILE, - CONF_PID_FILE, - CONF_STATE_FILE, - CONF_STATE_FILE_INTERVAL, - CONF_RESTORE_PAUSED, - CONF_USER, - CONF_GROUP, - CONF_BIND_TO_ADDRESS, - CONF_PORT, - CONF_LOG_LEVEL, - CONF_ZEROCONF_NAME, - CONF_ZEROCONF_ENABLED, - CONF_PASSWORD, - CONF_DEFAULT_PERMS, - CONF_AUDIO_OUTPUT, - CONF_AUDIO_OUTPUT_FORMAT, - CONF_MIXER_TYPE, - CONF_REPLAYGAIN, - CONF_REPLAYGAIN_PREAMP, - CONF_REPLAYGAIN_MISSING_PREAMP, - CONF_REPLAYGAIN_LIMIT, - CONF_VOLUME_NORMALIZATION, - CONF_SAMPLERATE_CONVERTER, - CONF_AUDIO_BUFFER_SIZE, - CONF_BUFFER_BEFORE_PLAY, - CONF_HTTP_PROXY_HOST, - CONF_HTTP_PROXY_PORT, - CONF_HTTP_PROXY_USER, - CONF_HTTP_PROXY_PASSWORD, - CONF_CONN_TIMEOUT, - CONF_MAX_CONN, - CONF_MAX_PLAYLIST_LENGTH, - CONF_MAX_COMMAND_LIST_SIZE, - CONF_MAX_OUTPUT_BUFFER_SIZE, - CONF_FS_CHARSET, - CONF_ID3V1_ENCODING, - CONF_METADATA_TO_USE, - CONF_SAVE_ABSOLUTE_PATHS, - CONF_DECODER, - CONF_INPUT, - CONF_GAPLESS_MP3_PLAYBACK, - CONF_PLAYLIST_PLUGIN, - CONF_AUTO_UPDATE, - CONF_AUTO_UPDATE_DEPTH, - CONF_DESPOTIFY_USER, - CONF_DESPOTIFY_PASSWORD, - CONF_DESPOTIFY_HIGH_BITRATE, - CONF_AUDIO_FILTER, - CONF_DATABASE, - CONF_NEIGHBORS, - CONF_MAX +#if defined(WIN32) && CLANG_OR_GCC_VERSION(4,7) +/* "INPUT" is declared by winuser.h */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#endif + +enum class ConfigOption { + MUSIC_DIR, + PLAYLIST_DIR, + FOLLOW_INSIDE_SYMLINKS, + FOLLOW_OUTSIDE_SYMLINKS, + DB_FILE, + STICKER_FILE, + LOG_FILE, + PID_FILE, + STATE_FILE, + STATE_FILE_INTERVAL, + RESTORE_PAUSED, + USER, + GROUP, + BIND_TO_ADDRESS, + PORT, + LOG_LEVEL, + ZEROCONF_NAME, + ZEROCONF_ENABLED, + PASSWORD, + DEFAULT_PERMS, + AUDIO_OUTPUT_FORMAT, + MIXER_TYPE, + REPLAYGAIN, + REPLAYGAIN_PREAMP, + REPLAYGAIN_MISSING_PREAMP, + REPLAYGAIN_LIMIT, + VOLUME_NORMALIZATION, + SAMPLERATE_CONVERTER, + AUDIO_BUFFER_SIZE, + BUFFER_BEFORE_PLAY, + HTTP_PROXY_HOST, + HTTP_PROXY_PORT, + HTTP_PROXY_USER, + HTTP_PROXY_PASSWORD, + CONN_TIMEOUT, + MAX_CONN, + MAX_PLAYLIST_LENGTH, + MAX_COMMAND_LIST_SIZE, + MAX_OUTPUT_BUFFER_SIZE, + FS_CHARSET, + ID3V1_ENCODING, + METADATA_TO_USE, + SAVE_ABSOLUTE_PATHS, + GAPLESS_MP3_PLAYBACK, + AUTO_UPDATE, + AUTO_UPDATE_DEPTH, + DESPOTIFY_USER, + DESPOTIFY_PASSWORD, + DESPOTIFY_HIGH_BITRATE, + MAX }; +enum class ConfigBlockOption { + AUDIO_OUTPUT, + DECODER, + INPUT, + PLAYLIST_PLUGIN, + RESAMPLER, + AUDIO_FILTER, + DATABASE, + NEIGHBORS, + MAX +}; + +#if defined(WIN32) && CLANG_OR_GCC_VERSION(4,7) +#pragma GCC diagnostic pop +#endif + /** - * @return #CONF_MAX if not found + * @return #ConfigOption::MAX if not found */ gcc_pure enum ConfigOption ParseConfigOptionName(const char *name); +/** + * @return #ConfigOption::MAX if not found + */ +gcc_pure +enum ConfigBlockOption +ParseConfigBlockOptionName(const char *name); + #endif diff --git a/src/config/ConfigParser.cxx b/src/config/ConfigParser.cxx index 3535c9a13..40afe1edd 100644 --- a/src/config/ConfigParser.cxx +++ b/src/config/ConfigParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,8 +23,8 @@ bool get_bool(const char *value, bool *value_r) { - static const char *t[] = { "yes", "true", "1", nullptr }; - static const char *f[] = { "no", "false", "0", nullptr }; + static const char *const t[] = { "yes", "true", "1", nullptr }; + static const char *const f[] = { "no", "false", "0", nullptr }; if (string_array_contains(t, value)) { *value_r = true; diff --git a/src/config/ConfigParser.hxx b/src/config/ConfigParser.hxx index 06151b0bd..c696801f5 100644 --- a/src/config/ConfigParser.hxx +++ b/src/config/ConfigParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/config/ConfigPath.cxx b/src/config/ConfigPath.cxx index a3b3f83a5..c1e57b3ba 100644 --- a/src/config/ConfigPath.cxx +++ b/src/config/ConfigPath.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -70,7 +70,7 @@ GetHome(Error &error) static AllocatedPath GetConfiguredHome(Error &error) { - const char *user = config_get_string(CONF_USER, nullptr); + const char *user = config_get_string(ConfigOption::USER); return user != nullptr ? GetHome(user, error) : GetHome(error); diff --git a/src/config/ConfigPath.hxx b/src/config/ConfigPath.hxx index a5518a497..afd3e0435 100644 --- a/src/config/ConfigPath.hxx +++ b/src/config/ConfigPath.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/config/ConfigTemplates.cxx b/src/config/ConfigTemplates.cxx index 58ee56425..cd353dc19 100644 --- a/src/config/ConfigTemplates.cxx +++ b/src/config/ConfigTemplates.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,80 +19,110 @@ #include "ConfigTemplates.hxx" #include "ConfigOption.hxx" +#include "util/Macros.hxx" #include <string.h> -const ConfigTemplate config_templates[] = { - { "music_directory", false, false }, - { "playlist_directory", false, false }, - { "follow_inside_symlinks", false, false }, - { "follow_outside_symlinks", false, false }, - { "db_file", false, false }, - { "sticker_file", false, false }, - { "log_file", false, false }, - { "pid_file", false, false }, - { "state_file", false, false }, - { "state_file_interval", false, false }, - { "restore_paused", false, false }, - { "user", false, false }, - { "group", false, false }, - { "bind_to_address", true, false }, - { "port", false, false }, - { "log_level", false, false }, - { "zeroconf_name", false, false }, - { "zeroconf_enabled", false, false }, - { "password", true, false }, - { "default_permissions", false, false }, - { "audio_output", true, true }, - { "audio_output_format", false, false }, - { "mixer_type", false, false }, - { "replaygain", false, false }, - { "replaygain_preamp", false, false }, - { "replaygain_missing_preamp", false, false }, - { "replaygain_limit", false, false }, - { "volume_normalization", false, false }, - { "samplerate_converter", false, false }, - { "audio_buffer_size", false, false }, - { "buffer_before_play", false, false }, - { "http_proxy_host", false, false }, - { "http_proxy_port", false, false }, - { "http_proxy_user", false, false }, - { "http_proxy_password", false, false }, - { "connection_timeout", false, false }, - { "max_connections", false, false }, - { "max_playlist_length", false, false }, - { "max_command_list_size", false, false }, - { "max_output_buffer_size", false, false }, - { "filesystem_charset", false, false }, - { "id3v1_encoding", false, false }, - { "metadata_to_use", false, false }, - { "save_absolute_paths_in_playlists", false, false }, - { "decoder", true, true }, - { "input", true, true }, - { "gapless_mp3_playback", false, false }, - { "playlist_plugin", true, true }, - { "auto_update", false, false }, - { "auto_update_depth", false, false }, - { "despotify_user", false, false }, - { "despotify_password", false, false}, - { "despotify_high_bitrate", false, false }, - { "filter", true, true }, - { "database", false, true }, - { "neighbors", true, true }, +const ConfigTemplate config_param_templates[] = { + { "music_directory" }, + { "playlist_directory" }, + { "follow_inside_symlinks" }, + { "follow_outside_symlinks" }, + { "db_file" }, + { "sticker_file" }, + { "log_file" }, + { "pid_file" }, + { "state_file" }, + { "state_file_interval" }, + { "restore_paused" }, + { "user" }, + { "group" }, + { "bind_to_address", true }, + { "port" }, + { "log_level" }, + { "zeroconf_name" }, + { "zeroconf_enabled" }, + { "password", true }, + { "default_permissions" }, + { "audio_output_format" }, + { "mixer_type" }, + { "replaygain" }, + { "replaygain_preamp" }, + { "replaygain_missing_preamp" }, + { "replaygain_limit" }, + { "volume_normalization" }, + { "samplerate_converter" }, + { "audio_buffer_size" }, + { "buffer_before_play" }, + { "http_proxy_host", false, true }, + { "http_proxy_port", false, true }, + { "http_proxy_user", false, true }, + { "http_proxy_password", false, true }, + { "connection_timeout" }, + { "max_connections" }, + { "max_playlist_length" }, + { "max_command_list_size" }, + { "max_output_buffer_size" }, + { "filesystem_charset" }, + { "id3v1_encoding", false, true }, + { "metadata_to_use" }, + { "save_absolute_paths_in_playlists" }, + { "gapless_mp3_playback" }, + { "auto_update" }, + { "auto_update_depth" }, + { "despotify_user", false, true }, + { "despotify_password", false, true }, + { "despotify_high_bitrate", false, true }, }; -static constexpr unsigned n_config_templates = - sizeof(config_templates) / sizeof(config_templates[0]); +static constexpr unsigned n_config_param_templates = + ARRAY_SIZE(config_param_templates); -static_assert(n_config_templates == unsigned(CONF_MAX), - "Wrong number of config_templates"); +static_assert(n_config_param_templates == unsigned(ConfigOption::MAX), + "Wrong number of config_param_templates"); + +const ConfigTemplate config_block_templates[] = { + { "audio_output", true }, + { "decoder", true }, + { "input", true }, + { "playlist_plugin", true }, + { "resampler" }, + { "filter", true }, + { "database" }, + { "neighbors", true }, +}; + +static constexpr unsigned n_config_block_templates = + ARRAY_SIZE(config_block_templates); + +static_assert(n_config_block_templates == unsigned(ConfigBlockOption::MAX), + "Wrong number of config_block_templates"); + +gcc_pure +static inline unsigned +ParseConfigTemplateName(const ConfigTemplate templates[], unsigned count, + const char *name) +{ + unsigned i = 0; + for (; i < count; ++i) + if (strcmp(templates[i].name, name) == 0) + break; + + return i; +} ConfigOption ParseConfigOptionName(const char *name) { - for (unsigned i = 0; i < n_config_templates; ++i) - if (strcmp(config_templates[i].name, name) == 0) - return ConfigOption(i); + return ConfigOption(ParseConfigTemplateName(config_param_templates, + n_config_param_templates, + name)); +} - return CONF_MAX; +ConfigBlockOption +ParseConfigBlockOptionName(const char *name) +{ + return ConfigBlockOption(ParseConfigTemplateName(config_block_templates, + n_config_block_templates, + name)); } diff --git a/src/config/ConfigTemplates.hxx b/src/config/ConfigTemplates.hxx index 90d098dc0..3a18ebdc8 100644 --- a/src/config/ConfigTemplates.hxx +++ b/src/config/ConfigTemplates.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,9 +23,18 @@ struct ConfigTemplate { const char *const name; const bool repeatable; - const bool block; + + // TODO: print warning when a deprecated option is used + const bool deprecated; + + constexpr ConfigTemplate(const char *_name, + bool _repeatable=false, + bool _deprecated=false) + :name(_name), repeatable(_repeatable), + deprecated(_deprecated) {} }; -extern const ConfigTemplate config_templates[]; +extern const ConfigTemplate config_param_templates[]; +extern const ConfigTemplate config_block_templates[]; #endif diff --git a/src/config/Data.cxx b/src/config/Data.cxx new file mode 100644 index 000000000..52521e31a --- /dev/null +++ b/src/config/Data.cxx @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2015 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 "Data.hxx" +#include "Param.hxx" +#include "Block.hxx" + +void +ConfigData::Clear() +{ + for (auto &i : params) { + delete i; + i = nullptr; + } + + for (auto &i : blocks) { + delete i; + i = nullptr; + } +} diff --git a/src/config/Data.hxx b/src/config/Data.hxx new file mode 100644 index 000000000..6036c49e6 --- /dev/null +++ b/src/config/Data.hxx @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2003-2015 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_CONFIG_DATA_HXX +#define MPD_CONFIG_DATA_HXX + +#include "ConfigOption.hxx" + +#include <array> + +struct config_param; +struct ConfigBlock; + +struct ConfigData { + std::array<config_param *, std::size_t(ConfigOption::MAX)> params; + std::array<ConfigBlock *, std::size_t(ConfigBlockOption::MAX)> blocks; + + void Clear(); +}; + +#endif diff --git a/src/config/Param.cxx b/src/config/Param.cxx new file mode 100644 index 000000000..bfd9743d2 --- /dev/null +++ b/src/config/Param.cxx @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2003-2015 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 "Param.hxx" + +config_param::config_param(const char *_value, int _line) + :next(nullptr), value(_value), line(_line), used(false) {} + +config_param::~config_param() +{ + delete next; +} diff --git a/src/config/Param.hxx b/src/config/Param.hxx new file mode 100644 index 000000000..e6a039c2a --- /dev/null +++ b/src/config/Param.hxx @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2003-2015 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_CONFIG_PARAM_HXX +#define MPD_CONFIG_PARAM_HXX + +#include "check.h" +#include "Compiler.h" + +#include <string> + +struct config_param { + /** + * The next config_param with the same name. The destructor + * deletes the whole chain. + */ + struct config_param *next; + + std::string value; + + int line; + + /** + * This flag is false when nobody has queried the value of + * this option yet. + */ + bool used; + + explicit config_param(int _line=-1) + :next(nullptr), line(_line), used(false) {} + + gcc_nonnull_all + config_param(const char *_value, int _line=-1); + + config_param(const config_param &) = delete; + + ~config_param(); + + config_param &operator=(const config_param &) = delete; + + /** + * Determine if this is a "null" instance, i.e. an empty + * object that was synthesized and not loaded from a + * configuration file. + */ + bool IsNull() const { + return line < 0; + } +}; + +#endif diff --git a/src/db/Configured.cxx b/src/db/Configured.cxx index 625300d75..c71195769 100644 --- a/src/db/Configured.cxx +++ b/src/db/Configured.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,8 @@ #include "Configured.hxx" #include "DatabaseGlue.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" +#include "config/Block.hxx" #include "config/ConfigError.hxx" #include "fs/AllocatedPath.hxx" #include "fs/StandardDirectory.hxx" @@ -32,8 +33,8 @@ Database * CreateConfiguredDatabase(EventLoop &loop, DatabaseListener &listener, Error &error) { - const struct config_param *param = config_get_param(CONF_DATABASE); - const struct config_param *path = config_get_param(CONF_DB_FILE); + const auto *param = config_get_block(ConfigBlockOption::DATABASE); + const auto *path = config_get_param(ConfigOption::DB_FILE); if (param != nullptr && path != nullptr) { error.Format(config_domain, @@ -42,10 +43,10 @@ CreateConfiguredDatabase(EventLoop &loop, DatabaseListener &listener, return nullptr; } - struct config_param *allocated = nullptr; + ConfigBlock *allocated = nullptr; if (param == nullptr && path != nullptr) { - allocated = new config_param("database", path->line); + allocated = new ConfigBlock(path->line); allocated->AddBlockParam("path", path->value.c_str(), path->line); param = allocated; @@ -58,10 +59,14 @@ CreateConfiguredDatabase(EventLoop &loop, DatabaseListener &listener, if (cache_dir.IsNull()) return nullptr; - const auto db_file = AllocatedPath::Build(cache_dir, "mpd.db"); + const auto db_file = AllocatedPath::Build(cache_dir, + PATH_LITERAL("mpd.db")); + const auto db_file_utf8 = db_file.ToUTF8(); + if (db_file_utf8.empty()) + return nullptr; - allocated = new config_param("database"); - allocated->AddBlockParam("path", db_file.c_str(), -1); + allocated = new ConfigBlock(); + allocated->AddBlockParam("path", db_file_utf8.c_str(), -1); param = allocated; } diff --git a/src/db/Configured.hxx b/src/db/Configured.hxx index 5d25b701c..aed2c2bf4 100644 --- a/src/db/Configured.hxx +++ b/src/db/Configured.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Count.cxx b/src/db/Count.cxx index e5e244a6a..f97f2f288 100644 --- a/src/db/Count.cxx +++ b/src/db/Count.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,8 @@ #include "Count.hxx" #include "Selection.hxx" #include "Interface.hxx" -#include "client/Client.hxx" +#include "Partition.hxx" +#include "client/Response.hxx" #include "LightSong.hxx" #include "tag/Tag.hxx" @@ -40,26 +41,24 @@ class TagCountMap : public std::map<std::string, SearchStats> { }; static void -PrintSearchStats(Client &client, const SearchStats &stats) +PrintSearchStats(Response &r, const SearchStats &stats) { unsigned total_duration_s = std::chrono::duration_cast<std::chrono::seconds>(stats.total_duration).count(); - client_printf(client, - "songs: %u\n" - "playtime: %u\n", - stats.n_songs, total_duration_s); + r.Format("songs: %u\n" + "playtime: %u\n", + stats.n_songs, total_duration_s); } static void -Print(Client &client, TagType group, const TagCountMap &m) +Print(Response &r, TagType group, const TagCountMap &m) { assert(unsigned(group) < TAG_NUM_OF_ITEM_TYPES); for (const auto &i : m) { - client_printf(client, "%s: %s\n", - tag_item_names[group], i.first.c_str()); - PrintSearchStats(client, i.second); + r.Format("%s: %s\n", tag_item_names[group], i.first.c_str()); + PrintSearchStats(r, i.second); } } @@ -109,12 +108,12 @@ GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song) } bool -PrintSongCount(Client &client, const char *name, +PrintSongCount(Response &r, const Partition &partition, const char *name, const SongFilter *filter, TagType group, Error &error) { - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) return false; @@ -131,7 +130,7 @@ PrintSongCount(Client &client, const char *name, if (!db->Visit(selection, f, error)) return false; - PrintSearchStats(client, stats); + PrintSearchStats(r, stats); } else { /* group by the specified tag: store counts in a std::map */ @@ -144,7 +143,7 @@ PrintSongCount(Client &client, const char *name, if (!db->Visit(selection, f, error)) return false; - Print(client, group, map); + Print(r, group, map); } return true; diff --git a/src/db/Count.hxx b/src/db/Count.hxx index d22a3210d..4fc26aa7a 100644 --- a/src/db/Count.hxx +++ b/src/db/Count.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,13 +25,14 @@ #include <stdint.h> enum TagType : uint8_t; -class Client; +struct Partition; +class Response; class SongFilter; class Error; -gcc_nonnull(2) +gcc_nonnull(3) bool -PrintSongCount(Client &client, const char *name, +PrintSongCount(Response &r, const Partition &partition, const char *name, const SongFilter *filter, TagType group, Error &error); diff --git a/src/db/DatabaseError.cxx b/src/db/DatabaseError.cxx index e0cbdd6a3..cf1993de2 100644 --- a/src/db/DatabaseError.cxx +++ b/src/db/DatabaseError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseError.hxx b/src/db/DatabaseError.hxx index c71bbdfff..542548c73 100644 --- a/src/db/DatabaseError.hxx +++ b/src/db/DatabaseError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseGlue.cxx b/src/db/DatabaseGlue.cxx index ade5c95f3..193b672e9 100644 --- a/src/db/DatabaseGlue.cxx +++ b/src/db/DatabaseGlue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,17 +22,17 @@ #include "Registry.hxx" #include "DatabaseError.hxx" #include "util/Error.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "DatabasePlugin.hxx" #include <string.h> Database * DatabaseGlobalInit(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { const char *plugin_name = - param.GetBlockValue("plugin", "simple"); + block.GetBlockValue("plugin", "simple"); const DatabasePlugin *plugin = GetDatabasePluginByName(plugin_name); if (plugin == nullptr) { @@ -41,5 +41,5 @@ DatabaseGlobalInit(EventLoop &loop, DatabaseListener &listener, return nullptr; } - return plugin->create(loop, listener, param, error); + return plugin->create(loop, listener, block, error); } diff --git a/src/db/DatabaseGlue.hxx b/src/db/DatabaseGlue.hxx index 70b50def3..95401d54a 100644 --- a/src/db/DatabaseGlue.hxx +++ b/src/db/DatabaseGlue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ #include "Compiler.h" -struct config_param; +struct ConfigBlock; class EventLoop; class DatabaseListener; class Database; @@ -31,10 +31,10 @@ class Error; /** * Initialize the database library. * - * @param param the database configuration block + * @param block the database configuration block */ Database * DatabaseGlobalInit(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, Error &error); + const ConfigBlock &block, Error &error); #endif diff --git a/src/db/DatabaseListener.hxx b/src/db/DatabaseListener.hxx index 8b410c2f5..5af3bff90 100644 --- a/src/db/DatabaseListener.hxx +++ b/src/db/DatabaseListener.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseLock.cxx b/src/db/DatabaseLock.cxx index c0b5e4844..ab4628722 100644 --- a/src/db/DatabaseLock.cxx +++ b/src/db/DatabaseLock.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseLock.hxx b/src/db/DatabaseLock.hxx index 9d0b0c152..786c97899 100644 --- a/src/db/DatabaseLock.hxx +++ b/src/db/DatabaseLock.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabasePlaylist.cxx b/src/db/DatabasePlaylist.cxx index f1cfdc874..d1e042c6a 100644 --- a/src/db/DatabasePlaylist.cxx +++ b/src/db/DatabasePlaylist.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabasePlaylist.hxx b/src/db/DatabasePlaylist.hxx index 9dc3526bb..333a36dc2 100644 --- a/src/db/DatabasePlaylist.hxx +++ b/src/db/DatabasePlaylist.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabasePlugin.hxx b/src/db/DatabasePlugin.hxx index 831101786..116aa59d2 100644 --- a/src/db/DatabasePlugin.hxx +++ b/src/db/DatabasePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #ifndef MPD_DATABASE_PLUGIN_HXX #define MPD_DATABASE_PLUGIN_HXX -struct config_param; +struct ConfigBlock; class Error; class EventLoop; class DatabaseListener; @@ -47,7 +47,7 @@ struct DatabasePlugin { * Allocates and configures a database. */ Database *(*create)(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); constexpr bool RequireStorage() const { diff --git a/src/db/DatabasePrint.cxx b/src/db/DatabasePrint.cxx index 498aedf97..e5e7a84f3 100644 --- a/src/db/DatabasePrint.cxx +++ b/src/db/DatabasePrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,8 @@ #include "SongPrint.hxx" #include "TimePrint.hxx" #include "client/Client.hxx" +#include "client/Response.hxx" +#include "Partition.hxx" #include "tag/Tag.hxx" #include "LightSong.hxx" #include "LightDirectory.hxx" @@ -42,162 +44,190 @@ ApplyBaseFlag(const char *uri, bool base) } static void -PrintDirectoryURI(Client &client, bool base, const LightDirectory &directory) +PrintDirectoryURI(Response &r, bool base, const LightDirectory &directory) { - client_printf(client, "directory: %s\n", - ApplyBaseFlag(directory.GetPath(), base)); + r.Format("directory: %s\n", + ApplyBaseFlag(directory.GetPath(), base)); } static bool -PrintDirectoryBrief(Client &client, bool base, const LightDirectory &directory) +PrintDirectoryBrief(Response &r, bool base, const LightDirectory &directory) { if (!directory.IsRoot()) - PrintDirectoryURI(client, base, directory); + PrintDirectoryURI(r, base, directory); return true; } static bool -PrintDirectoryFull(Client &client, bool base, const LightDirectory &directory) +PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory) { if (!directory.IsRoot()) { - PrintDirectoryURI(client, base, directory); + PrintDirectoryURI(r, base, directory); if (directory.mtime > 0) - time_print(client, "Last-Modified", directory.mtime); + time_print(r, "Last-Modified", directory.mtime); } return true; } static void -print_playlist_in_directory(Client &client, bool base, +print_playlist_in_directory(Response &r, bool base, const char *directory, const char *name_utf8) { if (base || directory == nullptr) - client_printf(client, "playlist: %s\n", - ApplyBaseFlag(name_utf8, base)); + r.Format("playlist: %s\n", + ApplyBaseFlag(name_utf8, base)); else - client_printf(client, "playlist: %s/%s\n", - directory, name_utf8); + r.Format("playlist: %s/%s\n", + directory, name_utf8); } static void -print_playlist_in_directory(Client &client, bool base, +print_playlist_in_directory(Response &r, bool base, const LightDirectory *directory, const char *name_utf8) { if (base || directory == nullptr || directory->IsRoot()) - client_printf(client, "playlist: %s\n", name_utf8); + r.Format("playlist: %s\n", name_utf8); else - client_printf(client, "playlist: %s/%s\n", - directory->GetPath(), name_utf8); + r.Format("playlist: %s/%s\n", + directory->GetPath(), name_utf8); } static bool -PrintSongBrief(Client &client, bool base, const LightSong &song) +PrintSongBrief(Response &r, Partition &partition, + bool base, const LightSong &song) { - song_print_uri(client, song, base); + song_print_uri(r, partition, song, base); if (song.tag->has_playlist) /* this song file has an embedded CUE sheet */ - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, song.directory, song.uri); return true; } static bool -PrintSongFull(Client &client, bool base, const LightSong &song) +PrintSongFull(Response &r, Partition &partition, + bool base, const LightSong &song) { - song_print_info(client, song, base); + song_print_info(r, partition, song, base); if (song.tag->has_playlist) /* this song file has an embedded CUE sheet */ - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, song.directory, song.uri); return true; } static bool -PrintPlaylistBrief(Client &client, bool base, +PrintPlaylistBrief(Response &r, bool base, const PlaylistInfo &playlist, const LightDirectory &directory) { - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, &directory, playlist.name.c_str()); return true; } static bool -PrintPlaylistFull(Client &client, bool base, +PrintPlaylistFull(Response &r, bool base, const PlaylistInfo &playlist, const LightDirectory &directory) { - print_playlist_in_directory(client, base, + print_playlist_in_directory(r, base, &directory, playlist.name.c_str()); if (playlist.mtime > 0) - time_print(client, "Last-Modified", playlist.mtime); + time_print(r, "Last-Modified", playlist.mtime); return true; } bool -db_selection_print(Client &client, const DatabaseSelection &selection, - bool full, bool base, Error &error) +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, + bool full, bool base, + unsigned window_start, unsigned window_end, + Error &error) { - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) return false; + unsigned i = 0; + using namespace std::placeholders; const auto d = selection.filter == nullptr ? std::bind(full ? PrintDirectoryFull : PrintDirectoryBrief, - std::ref(client), base, _1) + std::ref(r), base, _1) : VisitDirectory(); - const auto s = std::bind(full ? PrintSongFull : PrintSongBrief, - std::ref(client), base, _1); + VisitSong s = std::bind(full ? PrintSongFull : PrintSongBrief, + std::ref(r), std::ref(partition), base, _1); const auto p = selection.filter == nullptr ? std::bind(full ? PrintPlaylistFull : PrintPlaylistBrief, - std::ref(client), base, _1, _2) + std::ref(r), base, _1, _2) : VisitPlaylist(); + if (window_start > 0 || + window_end < (unsigned)std::numeric_limits<int>::max()) + s = [s, window_start, window_end, &i](const LightSong &song, + Error &error2){ + const bool in_window = i >= window_start && i < window_end; + ++i; + return !in_window || s(song, error2); + }; + return db->Visit(selection, d, s, p, error); } +bool +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, + bool full, bool base, + Error &error) +{ + return db_selection_print(r, partition, selection, full, base, + 0, std::numeric_limits<int>::max(), + error); +} + static bool -PrintSongURIVisitor(Client &client, const LightSong &song) +PrintSongURIVisitor(Response &r, Partition &partition, const LightSong &song) { - song_print_uri(client, song); + song_print_uri(r, partition, song); return true; } static bool -PrintUniqueTag(Client &client, TagType tag_type, +PrintUniqueTag(Response &r, TagType tag_type, const Tag &tag) { const char *value = tag.GetValue(tag_type); assert(value != nullptr); - client_printf(client, "%s: %s\n", tag_item_names[tag_type], value); + r.Format("%s: %s\n", tag_item_names[tag_type], value); for (const auto &item : tag) if (item.type != tag_type) - client_printf(client, "%s: %s\n", - tag_item_names[item.type], item.value); + r.Format("%s: %s\n", + tag_item_names[item.type], item.value); return true; } bool -PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, +PrintUniqueTags(Response &r, Partition &partition, + unsigned type, tag_mask_t group_mask, const SongFilter *filter, Error &error) { - const Database *db = client.GetDatabase(error); + const Database *db = partition.GetDatabase(error); if (db == nullptr) return false; @@ -206,13 +236,13 @@ PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, if (type == LOCATE_TAG_FILE_TYPE) { using namespace std::placeholders; const auto f = std::bind(PrintSongURIVisitor, - std::ref(client), _1); + std::ref(r), std::ref(partition), _1); return db->Visit(selection, f, error); } else { assert(type < TAG_NUM_OF_ITEM_TYPES); using namespace std::placeholders; - const auto f = std::bind(PrintUniqueTag, std::ref(client), + const auto f = std::bind(PrintUniqueTag, std::ref(r), (TagType)type, _1); return db->VisitUniqueTags(selection, (TagType)type, group_mask, diff --git a/src/db/DatabasePrint.hxx b/src/db/DatabasePrint.hxx index 2ab5e703d..d48dabaa2 100644 --- a/src/db/DatabasePrint.hxx +++ b/src/db/DatabasePrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,13 +20,14 @@ #ifndef MPD_DB_PRINT_H #define MPD_DB_PRINT_H +#include "tag/Mask.hxx" #include "Compiler.h" -#include <stdint.h> - class SongFilter; struct DatabaseSelection; +struct Partition; class Client; +class Response; class Error; /** @@ -34,11 +35,20 @@ class Error; * @param base print only base name of songs/directories? */ bool -db_selection_print(Client &client, const DatabaseSelection &selection, +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, bool full, bool base, Error &error); bool -PrintUniqueTags(Client &client, unsigned type, uint32_t group_mask, +db_selection_print(Response &r, Partition &partition, + const DatabaseSelection &selection, + bool full, bool base, + unsigned window_start, unsigned window_end, + Error &error); + +bool +PrintUniqueTags(Response &r, Partition &partition, + unsigned type, tag_mask_t group_mask, const SongFilter *filter, Error &error); diff --git a/src/db/DatabaseQueue.cxx b/src/db/DatabaseQueue.cxx index 490678188..c605e0878 100644 --- a/src/db/DatabaseQueue.cxx +++ b/src/db/DatabaseQueue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseQueue.hxx b/src/db/DatabaseQueue.hxx index e653f973c..e9ec7bde2 100644 --- a/src/db/DatabaseQueue.hxx +++ b/src/db/DatabaseQueue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseSong.cxx b/src/db/DatabaseSong.cxx index dd27aa8b3..d2475841b 100644 --- a/src/db/DatabaseSong.cxx +++ b/src/db/DatabaseSong.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/DatabaseSong.hxx b/src/db/DatabaseSong.hxx index 4daaf4047..c83f9562a 100644 --- a/src/db/DatabaseSong.hxx +++ b/src/db/DatabaseSong.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Helpers.cxx b/src/db/Helpers.cxx index add4bb98e..d4c29f405 100644 --- a/src/db/Helpers.cxx +++ b/src/db/Helpers.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -46,7 +46,7 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums, for (const auto &item : tag) { switch (item.type) { case TAG_ARTIST: -#if defined(__clang__) || GCC_CHECK_VERSION(4,8) +#if CLANG_OR_GCC_VERSION(4,8) artists.emplace(item.value); #else artists.insert(item.value); @@ -54,7 +54,7 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums, break; case TAG_ALBUM: -#if defined(__clang__) || GCC_CHECK_VERSION(4,8) +#if CLANG_OR_GCC_VERSION(4,8) albums.emplace(item.value); #else albums.insert(item.value); diff --git a/src/db/Helpers.hxx b/src/db/Helpers.hxx index 651bac0e0..5fcab2d7f 100644 --- a/src/db/Helpers.hxx +++ b/src/db/Helpers.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Interface.hxx b/src/db/Interface.hxx index 152928c79..7b43efe24 100644 --- a/src/db/Interface.hxx +++ b/src/db/Interface.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,10 +22,10 @@ #include "Visitor.hxx" #include "tag/TagType.h" +#include "tag/Mask.hxx" #include "Compiler.h" #include <time.h> -#include <stdint.h> struct DatabasePlugin; struct DatabaseStats; @@ -107,7 +107,7 @@ public: * Visit all unique tag values. */ virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const = 0; diff --git a/src/db/LightDirectory.hxx b/src/db/LightDirectory.hxx index d134151a4..ad5d34590 100644 --- a/src/db/LightDirectory.hxx +++ b/src/db/LightDirectory.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/LightSong.cxx b/src/db/LightSong.cxx index 5cdebc133..9202b1b30 100644 --- a/src/db/LightSong.cxx +++ b/src/db/LightSong.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/LightSong.hxx b/src/db/LightSong.hxx index bbd449fbe..3c289cce0 100644 --- a/src/db/LightSong.hxx +++ b/src/db/LightSong.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/PlaylistInfo.hxx b/src/db/PlaylistInfo.hxx index baa6cc361..f319cbcb6 100644 --- a/src/db/PlaylistInfo.hxx +++ b/src/db/PlaylistInfo.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/PlaylistVector.cxx b/src/db/PlaylistVector.cxx index 82a3519d9..1fffbc375 100644 --- a/src/db/PlaylistVector.cxx +++ b/src/db/PlaylistVector.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/PlaylistVector.hxx b/src/db/PlaylistVector.hxx index accd4fd42..7cb192dc0 100644 --- a/src/db/PlaylistVector.hxx +++ b/src/db/PlaylistVector.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Registry.cxx b/src/db/Registry.cxx index 5681a9b82..dc7807774 100644 --- a/src/db/Registry.cxx +++ b/src/db/Registry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,10 +28,10 @@ const DatabasePlugin *const database_plugins[] = { &simple_db_plugin, -#ifdef HAVE_LIBMPDCLIENT +#ifdef ENABLE_LIBMPDCLIENT &proxy_db_plugin, #endif -#ifdef HAVE_LIBUPNP +#ifdef ENABLE_UPNP &upnp_db_plugin, #endif nullptr diff --git a/src/db/Registry.hxx b/src/db/Registry.hxx index 050842e21..9f672f25e 100644 --- a/src/db/Registry.hxx +++ b/src/db/Registry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Selection.cxx b/src/db/Selection.cxx index a886916cb..4c941823a 100644 --- a/src/db/Selection.cxx +++ b/src/db/Selection.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,8 +26,11 @@ DatabaseSelection::DatabaseSelection(const char *_uri, bool _recursive, { /* optimization: if the caller didn't specify a base URI, pick the one from SongFilter */ - if (uri.empty() && filter != nullptr) - uri = filter->GetBase(); + if (uri.empty() && filter != nullptr) { + auto base = filter->GetBase(); + if (base != nullptr) + uri = base; + } } bool diff --git a/src/db/Selection.hxx b/src/db/Selection.hxx index 9802603fc..7216f5017 100644 --- a/src/db/Selection.hxx +++ b/src/db/Selection.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Stats.hxx b/src/db/Stats.hxx index 131a5dc47..c16dcc9c6 100644 --- a/src/db/Stats.hxx +++ b/src/db/Stats.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/UniqueTags.cxx b/src/db/UniqueTags.cxx index 589dc936d..a0c85cb85 100644 --- a/src/db/UniqueTags.cxx +++ b/src/db/UniqueTags.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #include <assert.h> static bool -CollectTags(TagSet &set, TagType tag_type, uint32_t group_mask, +CollectTags(TagSet &set, TagType tag_type, tag_mask_t group_mask, const LightSong &song) { assert(song.tag != nullptr); @@ -39,7 +39,7 @@ CollectTags(TagSet &set, TagType tag_type, uint32_t group_mask, bool VisitUniqueTags(const Database &db, const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) { diff --git a/src/db/UniqueTags.hxx b/src/db/UniqueTags.hxx index 61004fc56..b241970f9 100644 --- a/src/db/UniqueTags.hxx +++ b/src/db/UniqueTags.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,8 +22,7 @@ #include "Visitor.hxx" #include "tag/TagType.h" - -#include <stdint.h> +#include "tag/Mask.hxx" class Error; class Database; @@ -31,7 +30,7 @@ struct DatabaseSelection; bool VisitUniqueTags(const Database &db, const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error); diff --git a/src/db/Uri.hxx b/src/db/Uri.hxx index 04960ba80..12acabe4a 100644 --- a/src/db/Uri.hxx +++ b/src/db/Uri.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/Visitor.hxx b/src/db/Visitor.hxx index c524f1722..1f6008117 100644 --- a/src/db/Visitor.hxx +++ b/src/db/Visitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/LazyDatabase.cxx b/src/db/plugins/LazyDatabase.cxx index bc52395c5..9d57301a9 100644 --- a/src/db/plugins/LazyDatabase.cxx +++ b/src/db/plugins/LazyDatabase.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -85,7 +85,7 @@ LazyDatabase::Visit(const DatabaseSelection &selection, bool LazyDatabase::VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const { diff --git a/src/db/plugins/LazyDatabase.hxx b/src/db/plugins/LazyDatabase.hxx index 38b3fdc2a..6955aefe8 100644 --- a/src/db/plugins/LazyDatabase.hxx +++ b/src/db/plugins/LazyDatabase.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -52,7 +52,7 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const override; diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx index 5fd224bb5..8beb2fb55 100644 --- a/src/db/plugins/ProxyDatabasePlugin.cxx +++ b/src/db/plugins/ProxyDatabasePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ #include "db/Stats.hxx" #include "SongFilter.hxx" #include "Compiler.h" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "tag/TagBuilder.hxx" #include "tag/Tag.hxx" #include "util/Error.hxx" @@ -71,6 +71,7 @@ class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor { std::string host; unsigned port; + bool keepalive; struct mpd_connection *connection; @@ -96,7 +97,7 @@ public: listener(_listener) {} static Database *Create(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); virtual bool Open(Error &error) override; @@ -112,7 +113,7 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const override; @@ -128,7 +129,7 @@ public: } private: - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); bool Connect(Error &error); bool CheckConnection(Error &error); @@ -262,18 +263,18 @@ SendConstraints(mpd_connection *connection, const SongFilter::Item &item) return mpd_search_add_base_constraint(connection, MPD_OPERATOR_DEFAULT, - item.GetValue().c_str()); + item.GetValue()); #endif case LOCATE_TAG_FILE_TYPE: return mpd_search_add_uri_constraint(connection, MPD_OPERATOR_DEFAULT, - item.GetValue().c_str()); + item.GetValue()); case LOCATE_TAG_ANY_TYPE: return mpd_search_add_any_tag_constraint(connection, MPD_OPERATOR_DEFAULT, - item.GetValue().c_str()); + item.GetValue()); default: tag = Convert(TagType(item.GetTag())); @@ -283,7 +284,7 @@ SendConstraints(mpd_connection *connection, const SongFilter::Item &item) return mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT, tag, - item.GetValue().c_str()); + item.GetValue()); } } @@ -320,10 +321,10 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection) Database * ProxyDatabase::Create(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { ProxyDatabase *db = new ProxyDatabase(loop, listener); - if (!db->Configure(param, error)) { + if (!db->Configure(block, error)) { delete db; db = nullptr; } @@ -332,10 +333,11 @@ ProxyDatabase::Create(EventLoop &loop, DatabaseListener &listener, } bool -ProxyDatabase::Configure(const config_param ¶m, gcc_unused Error &error) +ProxyDatabase::Configure(const ConfigBlock &block, gcc_unused Error &error) { - host = param.GetBlockValue("host", ""); - port = param.GetBlockValue("port", 0u); + host = block.GetBlockValue("host", ""); + port = block.GetBlockValue("port", 0u); + keepalive = block.GetBlockValue("keepalive", false); return true; } @@ -376,6 +378,10 @@ ProxyDatabase::Connect(Error &error) return false; } +#if LIBMPDCLIENT_CHECK_VERSION(2, 10, 0) + mpd_connection_set_keepalive(connection, keepalive); +#endif + idle_received = unsigned(-1); is_idle = false; @@ -751,7 +757,7 @@ ProxyDatabase::Visit(const DatabaseSelection &selection, bool ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, TagType tag_type, - gcc_unused uint32_t group_mask, + gcc_unused tag_mask_t group_mask, VisitTag visit_tag, Error &error) const { diff --git a/src/db/plugins/ProxyDatabasePlugin.hxx b/src/db/plugins/ProxyDatabasePlugin.hxx index 699d374b5..590e5f5d2 100644 --- a/src/db/plugins/ProxyDatabasePlugin.hxx +++ b/src/db/plugins/ProxyDatabasePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/DatabaseSave.cxx b/src/db/plugins/simple/DatabaseSave.cxx index c766843b6..d2953b048 100644 --- a/src/db/plugins/simple/DatabaseSave.cxx +++ b/src/db/plugins/simple/DatabaseSave.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #include "fs/io/BufferedOutputStream.hxx" #include "fs/io/TextFile.hxx" #include "tag/Tag.hxx" -#include "tag/TagSettings.h" +#include "tag/Settings.hxx" #include "fs/Charset.hxx" #include "util/StringUtil.hxx" #include "util/Error.hxx" @@ -58,7 +58,7 @@ db_save_internal(BufferedOutputStream &os, const Directory &music_root) os.Format("%s%s\n", DIRECTORY_FS_CHARSET, GetFSCharset()); for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) - if (!ignore_tag_items[i]) + if (IsTagEnabled(i)) os.Format(DB_TAG_PREFIX "%s\n", tag_item_names[i]); os.Format("%s\n", DIRECTORY_INFO_END); @@ -142,7 +142,7 @@ db_load_internal(TextFile &file, Directory &music_root, Error &error) } for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) { - if (!ignore_tag_items[i] && !tags[i]) { + if (IsTagEnabled(i) && !tags[i]) { error.Set(db_domain, "Tag list mismatch, " "discarding database file"); diff --git a/src/db/plugins/simple/DatabaseSave.hxx b/src/db/plugins/simple/DatabaseSave.hxx index bb7f57115..5a8a60a92 100644 --- a/src/db/plugins/simple/DatabaseSave.hxx +++ b/src/db/plugins/simple/DatabaseSave.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/Directory.cxx b/src/db/plugins/simple/Directory.cxx index 218652b03..0c2b19efb 100644 --- a/src/db/plugins/simple/Directory.cxx +++ b/src/db/plugins/simple/Directory.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -31,6 +31,7 @@ #include "lib/icu/Collate.hxx" #include "fs/Traits.hxx" #include "util/Alloc.hxx" +#include "util/DeleteDisposer.hxx" #include "util/Error.hxx" #include <assert.h> @@ -51,7 +52,7 @@ Directory::~Directory() delete mounted_database; songs.clear_and_dispose(Song::Disposer()); - children.clear_and_dispose(Disposer()); + children.clear_and_dispose(DeleteDisposer()); } void @@ -61,7 +62,7 @@ Directory::Delete() assert(parent != nullptr); parent->children.erase_and_dispose(parent->children.iterator_to(*this), - Disposer()); + DeleteDisposer()); } const char * @@ -110,7 +111,8 @@ Directory::PruneEmpty() child->PruneEmpty(); if (child->IsEmpty()) - child = children.erase_and_dispose(child, Disposer()); + child = children.erase_and_dispose(child, + DeleteDisposer()); else ++child; } diff --git a/src/db/plugins/simple/Directory.hxx b/src/db/plugins/simple/Directory.hxx index acef62143..bad4647a0 100644 --- a/src/db/plugins/simple/Directory.hxx +++ b/src/db/plugins/simple/Directory.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -53,12 +53,6 @@ struct Directory { typedef boost::intrusive::link_mode<link_mode> LinkMode; typedef boost::intrusive::list_member_hook<LinkMode> Hook; - struct Disposer { - void operator()(Directory *directory) const { - delete directory; - } - }; - /** * Pointers to the siblings of this directory within the * parent directory. It is unused (undefined) in the root diff --git a/src/db/plugins/simple/DirectorySave.cxx b/src/db/plugins/simple/DirectorySave.cxx index e1650cbe8..5a7eb6da6 100644 --- a/src/db/plugins/simple/DirectorySave.cxx +++ b/src/db/plugins/simple/DirectorySave.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/DirectorySave.hxx b/src/db/plugins/simple/DirectorySave.hxx index f464f9946..3948db02b 100644 --- a/src/db/plugins/simple/DirectorySave.hxx +++ b/src/db/plugins/simple/DirectorySave.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/Mount.cxx b/src/db/plugins/simple/Mount.cxx index 96c7bbb5c..8898a6e9c 100644 --- a/src/db/plugins/simple/Mount.cxx +++ b/src/db/plugins/simple/Mount.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/Mount.hxx b/src/db/plugins/simple/Mount.hxx index a4690114c..ece644b8a 100644 --- a/src/db/plugins/simple/Mount.hxx +++ b/src/db/plugins/simple/Mount.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/PrefixedLightSong.hxx b/src/db/plugins/simple/PrefixedLightSong.hxx index 3664de001..16da747e3 100644 --- a/src/db/plugins/simple/PrefixedLightSong.hxx +++ b/src/db/plugins/simple/PrefixedLightSong.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx index d6ad5e91f..1e9817417 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -34,14 +34,15 @@ #include "fs/io/TextFile.hxx" #include "fs/io/BufferedOutputStream.hxx" #include "fs/io/FileOutputStream.hxx" -#include "config/ConfigData.hxx" +#include "fs/FileInfo.hxx" +#include "config/Block.hxx" #include "fs/FileSystem.hxx" #include "util/CharUtil.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB #include "fs/io/GzipOutputStream.hxx" #endif @@ -52,21 +53,21 @@ static constexpr Domain simple_db_domain("simple_db"); inline SimpleDatabase::SimpleDatabase() :Database(simple_db_plugin), path(AllocatedPath::Null()), -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB compress(true), #endif cache_path(AllocatedPath::Null()), prefixed_light_song(nullptr) {} inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path, -#ifndef HAVE_ZLIB +#ifndef ENABLE_ZLIB gcc_unused #endif bool _compress) :Database(simple_db_plugin), path(std::move(_path)), path_utf8(path.ToUTF8()), -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB compress(_compress), #endif cache_path(AllocatedPath::Null()), @@ -76,10 +77,10 @@ inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path, Database * SimpleDatabase::Create(gcc_unused EventLoop &loop, gcc_unused DatabaseListener &listener, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { SimpleDatabase *db = new SimpleDatabase(); - if (!db->Configure(param, error)) { + if (!db->Configure(block, error)) { delete db; db = nullptr; } @@ -88,9 +89,9 @@ SimpleDatabase::Create(gcc_unused EventLoop &loop, } bool -SimpleDatabase::Configure(const config_param ¶m, Error &error) +SimpleDatabase::Configure(const ConfigBlock &block, Error &error) { - path = param.GetBlockPath("path", error); + path = block.GetBlockPath("path", error); if (path.IsNull()) { if (!error.IsDefined()) error.Set(simple_db_domain, @@ -100,12 +101,12 @@ SimpleDatabase::Configure(const config_param ¶m, Error &error) path_utf8 = path.ToUTF8(); - cache_path = param.GetBlockPath("cache_directory", error); + cache_path = block.GetBlockPath("cache_directory", error); if (path.IsNull() && error.IsDefined()) return false; -#ifdef HAVE_ZLIB - compress = param.GetBlockValue("compress", compress); +#ifdef ENABLE_ZLIB + compress = block.GetBlockValue("compress", compress); #endif return true; @@ -117,22 +118,20 @@ SimpleDatabase::Check(Error &error) const assert(!path.IsNull()); /* Check if the file exists */ - if (!CheckAccess(path)) { + if (!PathExists(path)) { /* If the file doesn't exist, we can't check if we can write * it, so we are going to try to get the directory path, and * see if we can write a file in that */ const auto dirPath = path.GetDirectoryName(); /* Check that the parent part of the path is a directory */ - struct stat st; - if (!StatFile(dirPath, st)) { - error.FormatErrno("Couldn't stat parent directory of db file " - "\"%s\"", - path_utf8.c_str()); + FileInfo fi; + if (!GetFileInfo(dirPath, fi, error)) { + error.AddPrefix("On parent directory of db file: "); return false; } - if (!S_ISDIR(st.st_mode)) { + if (!fi.IsDirectory()) { error.Format(simple_db_domain, "Couldn't create db file \"%s\" because the " "parent path is not a directory", @@ -154,14 +153,11 @@ SimpleDatabase::Check(Error &error) const } /* Path exists, now check if it's a regular file */ - struct stat st; - if (!StatFile(path, st)) { - error.FormatErrno("Couldn't stat db file \"%s\"", - path_utf8.c_str()); + FileInfo fi; + if (!GetFileInfo(path, fi, error)) return false; - } - if (!S_ISREG(st.st_mode)) { + if (!fi.IsRegular()) { error.Format(simple_db_domain, "db file \"%s\" is not a regular file", path_utf8.c_str()); @@ -193,9 +189,9 @@ SimpleDatabase::Load(Error &error) if (!db_load_internal(file, *root, error) || !file.Check(error)) return false; - struct stat st; - if (StatFile(path, st)) - mtime = st.st_mtime; + FileInfo fi; + if (GetFileInfo(path, fi)) + mtime = fi.GetModificationTime(); return true; } @@ -352,7 +348,7 @@ SimpleDatabase::Visit(const DatabaseSelection &selection, bool SimpleDatabase::VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const { @@ -389,7 +385,7 @@ SimpleDatabase::Save(Error &error) OutputStream *os = &fos; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB GzipOutputStream *gzip = nullptr; if (compress) { gzip = new GzipOutputStream(*os, error); @@ -407,13 +403,13 @@ SimpleDatabase::Save(Error &error) db_save_internal(bos, *root); if (!bos.Flush(error)) { -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB delete gzip; #endif return false; } -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB if (gzip != nullptr) { bool success = gzip->Flush(error); delete gzip; @@ -425,9 +421,9 @@ SimpleDatabase::Save(Error &error) if (!fos.Commit(error)) return false; - struct stat st; - if (StatFile(path, st)) - mtime = st.st_mtime; + FileInfo fi; + if (GetFileInfo(path, fi)) + mtime = fi.GetModificationTime(); return true; } @@ -487,11 +483,15 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri, std::string name(storage_uri); std::replace_if(name.begin(), name.end(), IsUnsafeChar, '_'); -#ifndef HAVE_ZLIB + const auto name_fs = AllocatedPath::FromUTF8(name.c_str(), error); + if (name_fs.IsNull()) + return false; + +#ifndef ENABLE_ZLIB constexpr bool compress = false; #endif auto db = new SimpleDatabase(AllocatedPath::Build(cache_path, - name.c_str()), + name_fs.c_str()), compress); if (!db->Open(error)) { delete db; diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.hxx b/src/db/plugins/simple/SimpleDatabasePlugin.hxx index d82225f8c..7a28099d9 100644 --- a/src/db/plugins/simple/SimpleDatabasePlugin.hxx +++ b/src/db/plugins/simple/SimpleDatabasePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ #include <cassert> -struct config_param; +struct ConfigBlock; struct Directory; struct DatabasePlugin; class EventLoop; @@ -39,7 +39,7 @@ class SimpleDatabase : public Database { AllocatedPath path; std::string path_utf8; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB bool compress; #endif @@ -73,7 +73,7 @@ class SimpleDatabase : public Database { public: static Database *Create(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); gcc_pure @@ -121,7 +121,7 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const override; @@ -134,7 +134,7 @@ public: } private: - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); gcc_pure bool Check(Error &error) const; diff --git a/src/db/plugins/simple/Song.cxx b/src/db/plugins/simple/Song.cxx index fbfc2ec19..b2515a2d5 100644 --- a/src/db/plugins/simple/Song.cxx +++ b/src/db/plugins/simple/Song.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/Song.hxx b/src/db/plugins/simple/Song.hxx index 9f3a4a3ef..c83bbace0 100644 --- a/src/db/plugins/simple/Song.hxx +++ b/src/db/plugins/simple/Song.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #ifndef MPD_SONG_HXX #define MPD_SONG_HXX +#include "check.h" #include "Chrono.hxx" #include "tag/Tag.hxx" #include "Compiler.h" @@ -109,7 +110,10 @@ struct Song { void Free(); bool UpdateFile(Storage &storage); + +#ifdef ENABLE_ARCHIVE bool UpdateFileInArchive(const Storage &storage); +#endif /** * Returns the URI of the song in UTF-8 encoding, including its diff --git a/src/db/plugins/simple/SongSort.cxx b/src/db/plugins/simple/SongSort.cxx index 4b7144937..70299a2d4 100644 --- a/src/db/plugins/simple/SongSort.cxx +++ b/src/db/plugins/simple/SongSort.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/simple/SongSort.hxx b/src/db/plugins/simple/SongSort.hxx index 2a0c4383b..0a75c9fda 100644 --- a/src/db/plugins/simple/SongSort.hxx +++ b/src/db/plugins/simple/SongSort.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx index 88d4bd644..d98559c7b 100644 --- a/src/db/plugins/upnp/ContentDirectoryService.cxx +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/upnp/Directory.cxx b/src/db/plugins/upnp/Directory.cxx index e94a1a997..e7f92432a 100644 --- a/src/db/plugins/upnp/Directory.cxx +++ b/src/db/plugins/upnp/Directory.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "tag/TagBuilder.hxx" #include "tag/TagTable.hxx" #include "util/NumberParser.hxx" +#include "util/StringView.hxx" #include <algorithm> #include <string> @@ -36,23 +37,13 @@ UPnPDirContent::~UPnPDirContent() /* this destructor exists here just so it won't get inlined */ } -gcc_pure gcc_nonnull_all -static bool -CompareStringLiteral(const char *literal, const char *value, size_t length) -{ - return length == strlen(literal) && - memcmp(literal, value, length) == 0; -} - gcc_pure static UPnPDirObject::ItemClass -ParseItemClass(const char *name, size_t length) +ParseItemClass(StringView name) { - if (CompareStringLiteral("object.item.audioItem.musicTrack", - name, length)) + if (name.EqualsLiteral("object.item.audioItem.musicTrack")) return UPnPDirObject::ItemClass::MUSIC; - else if (CompareStringLiteral("object.item.playlistItem", - name, length)) + else if (name.EqualsLiteral("object.item.playlistItem")) return UPnPDirObject::ItemClass::PLAYLIST; else return UPnPDirObject::ItemClass::UNKNOWN; @@ -89,18 +80,18 @@ ParseDuration(const char *duration) * this. Twonky returns directory names (titles) like 'Artist/Album'. */ gcc_pure -static std::string -titleToPathElt(std::string &&s) +static std::string && +TitleToPathSegment(std::string &&s) { std::replace(s.begin(), s.end(), '/', '_'); - return s; + return std::move(s); } /** * An XML parser which builds directory contents from DIDL lite input. */ class UPnPDirParser final : public CommonExpatParser { - UPnPDirContent &m_dir; + UPnPDirContent &directory; enum { NONE, @@ -120,22 +111,22 @@ class UPnPDirParser final : public CommonExpatParser { */ std::string value; - UPnPDirObject m_tobj; + UPnPDirObject object; TagBuilder tag; public: - UPnPDirParser(UPnPDirContent& dir) - :m_dir(dir), + UPnPDirParser(UPnPDirContent &_directory) + :directory(_directory), state(NONE), tag_type(TAG_NUM_OF_ITEM_TYPES) { - m_tobj.clear(); + object.Clear(); } protected: virtual void StartElement(const XML_Char *name, const XML_Char **attrs) { - if (m_tobj.type != UPnPDirObject::Type::UNKNOWN && + if (object.type != UPnPDirObject::Type::UNKNOWN && tag_type == TAG_NUM_OF_ITEM_TYPES) { tag_type = tag_table_lookup(upnp_tags, name); if (tag_type != TAG_NUM_OF_ITEM_TYPES) @@ -147,31 +138,31 @@ protected: switch (name[0]) { case 'c': if (!strcmp(name, "container")) { - m_tobj.clear(); - m_tobj.type = UPnPDirObject::Type::CONTAINER; + object.Clear(); + object.type = UPnPDirObject::Type::CONTAINER; const char *id = GetAttribute(attrs, "id"); if (id != nullptr) - m_tobj.m_id = id; + object.id = id; const char *pid = GetAttribute(attrs, "parentID"); if (pid != nullptr) - m_tobj.m_pid = pid; + object.parent_id = pid; } break; case 'i': if (!strcmp(name, "item")) { - m_tobj.clear(); - m_tobj.type = UPnPDirObject::Type::ITEM; + object.Clear(); + object.type = UPnPDirObject::Type::ITEM; const char *id = GetAttribute(attrs, "id"); if (id != nullptr) - m_tobj.m_id = id; + object.id = id; const char *pid = GetAttribute(attrs, "parentID"); if (pid != nullptr) - m_tobj.m_pid = pid; + object.parent_id = pid; } break; @@ -197,25 +188,15 @@ protected: } } - bool checkobjok() { - if (m_tobj.m_id.empty() || m_tobj.m_pid.empty() || - m_tobj.name.empty() || - (m_tobj.type == UPnPDirObject::Type::ITEM && - m_tobj.item_class == UPnPDirObject::ItemClass::UNKNOWN)) - return false; - - return true; - } - virtual void EndElement(const XML_Char *name) { if (tag_type != TAG_NUM_OF_ITEM_TYPES) { - assert(m_tobj.type != UPnPDirObject::Type::UNKNOWN); + assert(object.type != UPnPDirObject::Type::UNKNOWN); tag.AddItem(tag_type, value.c_str()); if (tag_type == TAG_TITLE) - m_tobj.name = titleToPathElt(std::move(value)); + object.name = TitleToPathSegment(std::move(value)); value.clear(); tag_type = TAG_NUM_OF_ITEM_TYPES; @@ -223,9 +204,9 @@ protected: } if ((!strcmp(name, "container") || !strcmp(name, "item")) && - checkobjok()) { - tag.Commit(m_tobj.tag); - m_dir.objects.emplace_back(std::move(m_tobj)); + object.Check()) { + tag.Commit(object.tag); + directory.objects.emplace_back(std::move(object)); } state = NONE; @@ -234,7 +215,7 @@ protected: virtual void CharacterData(const XML_Char *s, int len) { if (tag_type != TAG_NUM_OF_ITEM_TYPES) { - assert(m_tobj.type != UPnPDirObject::Type::UNKNOWN); + assert(object.type != UPnPDirObject::Type::UNKNOWN); value.append(s, len); return; @@ -245,11 +226,11 @@ protected: break; case RES: - m_tobj.url.assign(s, len); + object.url.assign(s, len); break; case CLASS: - m_tobj.item_class = ParseItemClass(s, len); + object.item_class = ParseItemClass(StringView(s, len)); break; } } diff --git a/src/db/plugins/upnp/Directory.hxx b/src/db/plugins/upnp/Directory.hxx index 433979900..639f2bcbe 100644 --- a/src/db/plugins/upnp/Directory.hxx +++ b/src/db/plugins/upnp/Directory.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/upnp/Object.cxx b/src/db/plugins/upnp/Object.cxx index 703fb0be4..0b5c4be88 100644 --- a/src/db/plugins/upnp/Object.cxx +++ b/src/db/plugins/upnp/Object.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/upnp/Object.hxx b/src/db/plugins/upnp/Object.hxx index 16a66c774..aced04b27 100644 --- a/src/db/plugins/upnp/Object.hxx +++ b/src/db/plugins/upnp/Object.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,7 @@ #define MPD_UPNP_OBJECT_HXX #include "tag/Tag.hxx" +#include "Compiler.h" #include <string> @@ -50,8 +51,16 @@ public: PLAYLIST, }; - std::string m_id; // ObjectId - std::string m_pid; // Parent ObjectId + /** + * ObjectId + */ + std::string id; + + /** + * Parent's ObjectId + */ + std::string parent_id; + std::string url; /** @@ -71,15 +80,21 @@ public: UPnPDirObject &operator=(UPnPDirObject &&) = default; - void clear() - { - m_id.clear(); - m_pid.clear(); + void Clear() { + id.clear(); + parent_id.clear(); url.clear(); type = Type::UNKNOWN; item_class = ItemClass::UNKNOWN; tag.Clear(); } + + gcc_pure + bool Check() const { + return !id.empty() && !parent_id.empty() && !name.empty() && + (type != UPnPDirObject::Type::ITEM || + item_class != UPnPDirObject::ItemClass::UNKNOWN); + } }; #endif /* _UPNPDIRCONTENT_H_X_INCLUDED_ */ diff --git a/src/db/plugins/upnp/Tags.cxx b/src/db/plugins/upnp/Tags.cxx index fd65df4d0..89d1b53c5 100644 --- a/src/db/plugins/upnp/Tags.cxx +++ b/src/db/plugins/upnp/Tags.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/upnp/Tags.hxx b/src/db/plugins/upnp/Tags.hxx index ec6d18478..8f85ce4b0 100644 --- a/src/db/plugins/upnp/Tags.hxx +++ b/src/db/plugins/upnp/Tags.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx index 9970cdcf3..26844a978 100644 --- a/src/db/plugins/upnp/UpnpDatabasePlugin.cxx +++ b/src/db/plugins/upnp/UpnpDatabasePlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ #include "db/LightDirectory.hxx" #include "db/LightSong.hxx" #include "db/Stats.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "tag/TagBuilder.hxx" #include "tag/TagTable.hxx" #include "util/Error.hxx" @@ -78,7 +78,7 @@ public: UpnpDatabase():Database(upnp_db_plugin) {} static Database *Create(EventLoop &loop, DatabaseListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); virtual bool Open(Error &error) override; @@ -94,7 +94,7 @@ public: Error &error) const override; virtual bool VisitUniqueTags(const DatabaseSelection &selection, - TagType tag_type, uint32_t group_mask, + TagType tag_type, tag_mask_t group_mask, VisitTag visit_tag, Error &error) const override; @@ -106,7 +106,7 @@ public: } protected: - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); private: bool VisitServer(const ContentDirectoryService &server, @@ -158,10 +158,10 @@ private: Database * UpnpDatabase::Create(gcc_unused EventLoop &loop, gcc_unused DatabaseListener &listener, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { UpnpDatabase *db = new UpnpDatabase(); - if (!db->Configure(param, error)) { + if (!db->Configure(block, error)) { delete db; return nullptr; } @@ -173,7 +173,7 @@ UpnpDatabase::Create(gcc_unused EventLoop &loop, } inline bool -UpnpDatabase::Configure(const config_param &, Error &) +UpnpDatabase::Configure(const ConfigBlock &, Error &) { return true; } @@ -222,7 +222,7 @@ UpnpDatabase::GetSong(const char *uri, Error &error) const } ContentDirectoryService server; - if (!discovery->getServer(vpath.front().c_str(), server, error)) + if (!discovery->GetServer(vpath.front().c_str(), server, error)) return nullptr; vpath.pop_front(); @@ -303,7 +303,7 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server, } else { cond += " = "; } - dquote(cond, item.GetValue().c_str()); + dquote(cond, item.GetValue()); } cond += ')'; } @@ -339,7 +339,7 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server, } else { cond += " = "; } - dquote(cond, item.GetValue().c_str()); + dquote(cond, item.GetValue()); } } @@ -414,7 +414,7 @@ UpnpDatabase::SearchSongs(const ContentDirectoryService &server, // So we return synthetic and ugly paths based on the object id, // which we later have to detect. const std::string path = songPath(server.getFriendlyName(), - dirent.m_id); + dirent.id); if (!visitSong(std::move(dirent), path.c_str(), selection, visit_song, error)) @@ -449,13 +449,13 @@ UpnpDatabase::BuildPath(const ContentDirectoryService &server, std::string &path, Error &error) const { - const char *pid = idirent.m_id.c_str(); + const char *pid = idirent.id.c_str(); path.clear(); UPnPDirObject dirent; while (strcmp(pid, rootid) != 0) { if (!ReadNode(server, pid, dirent, error)) return false; - pid = dirent.m_pid.c_str(); + pid = dirent.parent_id.c_str(); if (path.empty()) path = dirent.name; @@ -511,7 +511,7 @@ UpnpDatabase::Namei(const ContentDirectoryService &server, return false; } - objid = std::move(child->m_id); + objid = std::move(child->id); } } @@ -623,7 +623,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, } std::string path = songPath(server.getFriendlyName(), - dirent.m_id); + dirent.id); if (!visitSong(std::move(dirent), path.c_str(), selection, visit_song, error)) @@ -642,7 +642,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, recursion (1-deep) here, which will handle the "add dir" case. */ if (selection.recursive && selection.filter) - return SearchSongs(server, tdirent.m_id.c_str(), selection, + return SearchSongs(server, tdirent.id.c_str(), selection, visit_song, error); const char *const base_uri = selection.uri.empty() @@ -660,7 +660,7 @@ UpnpDatabase::VisitServer(const ContentDirectoryService &server, and loop here, but it's not useful as mpd will only return data to the client when we're done anyway. */ UPnPDirContent dirbuf; - if (!server.readDir(handle, tdirent.m_id.c_str(), dirbuf, + if (!server.readDir(handle, tdirent.id.c_str(), dirbuf, error)) return false; @@ -689,7 +689,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, auto vpath = stringToTokens(selection.uri, "/", true); if (vpath.empty()) { std::vector<ContentDirectoryService> servers; - if (!discovery->getDirServices(servers, error)) + if (!discovery->GetDirectories(servers, error)) return false; for (const auto &server : servers) { @@ -714,7 +714,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, vpath.pop_front(); ContentDirectoryService server; - if (!discovery->getServer(servername.c_str(), server, error)) + if (!discovery->GetServer(servername.c_str(), server, error)) return false; return VisitServer(server, vpath, selection, @@ -723,7 +723,7 @@ UpnpDatabase::Visit(const DatabaseSelection &selection, bool UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, - TagType tag, gcc_unused uint32_t group_mask, + TagType tag, gcc_unused tag_mask_t group_mask, VisitTag visit_tag, Error &error) const { @@ -733,7 +733,7 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, return true; std::vector<ContentDirectoryService> servers; - if (!discovery->getDirServices(servers, error)) + if (!discovery->GetDirectories(servers, error)) return false; std::set<std::string> values; @@ -749,7 +749,7 @@ UpnpDatabase::VisitUniqueTags(const DatabaseSelection &selection, const char *value = dirent.tag.GetValue(tag); if (value != nullptr) { -#if defined(__clang__) || GCC_CHECK_VERSION(4,8) +#if CLANG_OR_GCC_VERSION(4,8) values.emplace(value); #else values.insert(value); diff --git a/src/db/plugins/upnp/UpnpDatabasePlugin.hxx b/src/db/plugins/upnp/UpnpDatabasePlugin.hxx index 0228405cd..f03cde75d 100644 --- a/src/db/plugins/upnp/UpnpDatabasePlugin.hxx +++ b/src/db/plugins/upnp/UpnpDatabasePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Archive.cxx b/src/db/update/Archive.cxx index fc8f1fcbf..e818ab279 100644 --- a/src/db/update/Archive.cxx +++ b/src/db/update/Archive.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -104,7 +104,7 @@ class UpdateArchiveVisitor final : public ArchiveVisitor { */ void UpdateWalk::UpdateArchiveFile(Directory &parent, const char *name, - const FileInfo &info, + const StorageFileInfo &info, const ArchivePlugin &plugin) { db_lock(); @@ -156,7 +156,7 @@ UpdateWalk::UpdateArchiveFile(Directory &parent, const char *name, bool UpdateWalk::UpdateArchiveFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info) + const StorageFileInfo &info) { const ArchivePlugin *plugin = archive_plugin_from_suffix(suffix); if (plugin == nullptr) diff --git a/src/db/update/Container.cxx b/src/db/update/Container.cxx index 1c420fa99..93ba5ff20 100644 --- a/src/db/update/Container.cxx +++ b/src/db/update/Container.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ Directory * UpdateWalk::MakeDirectoryIfModified(Directory &parent, const char *name, - const FileInfo &info) + const StorageFileInfo &info) { Directory *directory = parent.FindChild(name); @@ -69,7 +69,7 @@ SupportsContainerSuffix(const DecoderPlugin &plugin, const char *suffix) bool UpdateWalk::UpdateContainerFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info) + const StorageFileInfo &info) { const DecoderPlugin *_plugin = decoder_plugins_find([suffix](const DecoderPlugin &plugin){ return SupportsContainerSuffix(plugin, suffix); @@ -106,8 +106,11 @@ UpdateWalk::UpdateContainerFile(Directory &directory, // shouldn't be necessary but it's there.. song->mtime = info.mtime; + const auto vtrack_fs = AllocatedPath::FromUTF8(vtrack); + // TODO: check vtrack_fs.IsNull() + const auto child_path_fs = AllocatedPath::Build(pathname, - vtrack); + vtrack_fs); plugin.ScanFile(child_path_fs, add_tag_handler, &tag_builder); diff --git a/src/db/update/Editor.cxx b/src/db/update/Editor.cxx index 4136ccdad..2ca17f8cb 100644 --- a/src/db/update/Editor.cxx +++ b/src/db/update/Editor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Editor.hxx b/src/db/update/Editor.hxx index 58e23ed7a..cefb6c07f 100644 --- a/src/db/update/Editor.hxx +++ b/src/db/update/Editor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/ExcludeList.cxx b/src/db/update/ExcludeList.cxx index cf92ac8f7..b09f349ac 100644 --- a/src/db/update/ExcludeList.cxx +++ b/src/db/update/ExcludeList.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,36 +25,46 @@ #include "config.h" #include "ExcludeList.hxx" #include "fs/Path.hxx" -#include "fs/FileSystem.hxx" +#include "fs/NarrowPath.hxx" +#include "fs/io/TextFile.hxx" #include "util/StringUtil.hxx" -#include "util/Domain.hxx" +#include "util/Error.hxx" #include "Log.hxx" #include <assert.h> #include <string.h> #include <errno.h> -static constexpr Domain exclude_list_domain("exclude_list"); +#ifdef HAVE_CLASS_GLOB + +gcc_pure +static bool +IsFileNotFound(const Error &error) +{ +#ifdef WIN32 + return error.IsDomain(win32_domain) && + error.GetCode() == ERROR_FILE_NOT_FOUND; +#else + return error.IsDomain(errno_domain) && error.GetCode() == ENOENT; +#endif +} + +#endif bool ExcludeList::LoadFile(Path path_fs) { -#ifdef HAVE_GLIB - FILE *file = FOpen(path_fs, FOpenMode::ReadText); - if (file == nullptr) { - const int e = errno; - if (e != ENOENT) { - const auto path_utf8 = path_fs.ToUTF8(); - FormatErrno(exclude_list_domain, - "Failed to open %s", - path_utf8.c_str()); - } - +#ifdef HAVE_CLASS_GLOB + Error error; + TextFile file(path_fs, error); + if (file.HasFailed()) { + if (!IsFileNotFound(error)) + LogError(error); return false; } - char line[1024]; - while (fgets(line, sizeof(line), file) != nullptr) { + char *line; + while ((line = file.ReadLine()) != nullptr) { char *p = strchr(line, '#'); if (p != nullptr) *p = 0; @@ -63,10 +73,8 @@ ExcludeList::LoadFile(Path path_fs) if (*p != 0) patterns.emplace_front(p); } - - fclose(file); #else - // TODO: implement + /* not implemented */ (void)path_fs; #endif @@ -80,12 +88,18 @@ ExcludeList::Check(Path name_fs) const /* XXX include full path name in check */ -#ifdef HAVE_GLIB +#ifdef HAVE_CLASS_GLOB + if (parent != nullptr) { + if (parent->Check(name_fs)) { + return true; + } + } + for (const auto &i : patterns) - if (i.Check(name_fs.c_str())) + if (i.Check(NarrowPath(name_fs).c_str())) return true; #else - // TODO: implement + /* not implemented */ (void)name_fs; #endif diff --git a/src/db/update/ExcludeList.hxx b/src/db/update/ExcludeList.hxx index ef6c4d53e..4952d291a 100644 --- a/src/db/update/ExcludeList.hxx +++ b/src/db/update/ExcludeList.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,51 +27,34 @@ #include "check.h" #include "Compiler.h" +#include "fs/Glob.hxx" +#ifdef HAVE_CLASS_GLOB #include <forward_list> - -#ifdef HAVE_GLIB -#include <glib.h> #endif class Path; class ExcludeList { -#ifdef HAVE_GLIB - class Pattern { - GPatternSpec *pattern; - - public: - Pattern(const char *_pattern) - :pattern(g_pattern_spec_new(_pattern)) {} - - Pattern(Pattern &&other) - :pattern(other.pattern) { - other.pattern = nullptr; - } - - ~Pattern() { - g_pattern_spec_free(pattern); - } + const ExcludeList *const parent; - gcc_pure - bool Check(const char *name_fs) const { - return g_pattern_match_string(pattern, name_fs); - } - }; - - std::forward_list<Pattern> patterns; -#else - // TODO: implement +#ifdef HAVE_CLASS_GLOB + std::forward_list<Glob> patterns; #endif public: + ExcludeList() + :parent(nullptr) {} + + ExcludeList(const ExcludeList &_parent) + :parent(&_parent) {} + gcc_pure bool IsEmpty() const { -#ifdef HAVE_GLIB - return patterns.empty(); +#ifdef HAVE_CLASS_GLOB + return ((parent == nullptr) || parent->IsEmpty()) && patterns.empty(); #else - // TODO: implement + /* not implemented */ return true; #endif } diff --git a/src/db/update/InotifyDomain.cxx b/src/db/update/InotifyDomain.cxx index 4a3ab2d79..18557a5ce 100644 --- a/src/db/update/InotifyDomain.cxx +++ b/src/db/update/InotifyDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/InotifyDomain.hxx b/src/db/update/InotifyDomain.hxx index ad6202361..219ee7e4f 100644 --- a/src/db/update/InotifyDomain.hxx +++ b/src/db/update/InotifyDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/InotifyQueue.cxx b/src/db/update/InotifyQueue.cxx index 013200f98..4b5269427 100644 --- a/src/db/update/InotifyQueue.cxx +++ b/src/db/update/InotifyQueue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/InotifyQueue.hxx b/src/db/update/InotifyQueue.hxx index a9abc2969..dab7c7e8c 100644 --- a/src/db/update/InotifyQueue.hxx +++ b/src/db/update/InotifyQueue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/InotifySource.cxx b/src/db/update/InotifySource.cxx index 233504ad6..b084dd60a 100644 --- a/src/db/update/InotifySource.cxx +++ b/src/db/update/InotifySource.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "InotifySource.hxx" #include "InotifyDomain.hxx" #include "util/Error.hxx" -#include "system/fd_util.h" +#include "system/FileDescriptor.hxx" #include "system/FatalError.hxx" #include "Log.hxx" @@ -72,8 +72,8 @@ InotifySource::OnSocketReady(gcc_unused unsigned flags) inline InotifySource::InotifySource(EventLoop &_loop, mpd_inotify_callback_t _callback, void *_ctx, - int _fd) - :SocketMonitor(_fd, _loop), + FileDescriptor _fd) + :SocketMonitor(_fd.Get(), _loop), callback(_callback), callback_ctx(_ctx) { ScheduleRead(); @@ -85,8 +85,8 @@ InotifySource::Create(EventLoop &loop, mpd_inotify_callback_t callback, void *callback_ctx, Error &error) { - int fd = inotify_init_cloexec(); - if (fd < 0) { + FileDescriptor fd; + if (!fd.CreateInotify()) { error.SetErrno("inotify_init() has failed"); return nullptr; } diff --git a/src/db/update/InotifySource.hxx b/src/db/update/InotifySource.hxx index 2557680a0..34d84c2dd 100644 --- a/src/db/update/InotifySource.hxx +++ b/src/db/update/InotifySource.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "Compiler.h" class Error; +class FileDescriptor; typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask, const char *name, void *ctx); @@ -33,7 +34,8 @@ class InotifySource final : private SocketMonitor { void *callback_ctx; InotifySource(EventLoop &_loop, - mpd_inotify_callback_t callback, void *ctx, int fd); + mpd_inotify_callback_t callback, void *ctx, + FileDescriptor fd); public: ~InotifySource() { @@ -41,10 +43,11 @@ public: } /** - * Creates a new inotify source and registers it in the GLib main - * loop. + * Creates a new inotify source and registers it in the + * #EventLoop. * - * @param a callback invoked for events received from the kernel + * @param callback a callback invoked for events received from + * the kernel */ static InotifySource *Create(EventLoop &_loop, mpd_inotify_callback_t callback, diff --git a/src/db/update/InotifyUpdate.cxx b/src/db/update/InotifyUpdate.cxx index 26bdddf8d..f699b7f78 100644 --- a/src/db/update/InotifyUpdate.cxx +++ b/src/db/update/InotifyUpdate.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,7 @@ #include "InotifyDomain.hxx" #include "storage/StorageInterface.hxx" #include "fs/AllocatedPath.hxx" -#include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -179,7 +179,6 @@ recursive_watch_subdirectories(WatchDirectory *directory, } while ((ent = readdir(dir))) { - struct stat st; int ret; if (skip_path(ent->d_name)) @@ -187,15 +186,15 @@ recursive_watch_subdirectories(WatchDirectory *directory, const auto child_path_fs = AllocatedPath::Build(path_fs, ent->d_name); - ret = StatFile(child_path_fs, st); - if (ret < 0) { - FormatErrno(inotify_domain, - "Failed to stat %s", - child_path_fs.c_str()); + + FileInfo fi; + if (!GetFileInfo(child_path_fs, fi, error)) { + LogError(error); + error.Clear(); continue; } - if (!S_ISDIR(st.st_mode)) + if (!fi.IsDirectory()) continue; ret = inotify_source->Add(child_path_fs.c_str(), IN_MASK, diff --git a/src/db/update/InotifyUpdate.hxx b/src/db/update/InotifyUpdate.hxx index 0f78db71f..82b6cdf04 100644 --- a/src/db/update/InotifyUpdate.hxx +++ b/src/db/update/InotifyUpdate.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -32,6 +32,6 @@ mpd_inotify_init(EventLoop &loop, Storage &storage, UpdateService &update, unsigned max_depth); void -mpd_inotify_finish(void); +mpd_inotify_finish(); #endif diff --git a/src/db/update/Queue.cxx b/src/db/update/Queue.cxx index 6d6d80131..870207fc6 100644 --- a/src/db/update/Queue.cxx +++ b/src/db/update/Queue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Queue.hxx b/src/db/update/Queue.hxx index 9064ea481..6c0a5733d 100644 --- a/src/db/update/Queue.hxx +++ b/src/db/update/Queue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Remove.cxx b/src/db/update/Remove.cxx index dfada05b2..0f1cbde1c 100644 --- a/src/db/update/Remove.cxx +++ b/src/db/update/Remove.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Remove.hxx b/src/db/update/Remove.hxx index ce6d77d47..5ebfb884b 100644 --- a/src/db/update/Remove.hxx +++ b/src/db/update/Remove.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Service.cxx b/src/db/update/Service.cxx index e8a1f6b02..e14dbdc6c 100644 --- a/src/db/update/Service.cxx +++ b/src/db/update/Service.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/Service.hxx b/src/db/update/Service.hxx index feaeaebc5..7dd198a7f 100644 --- a/src/db/update/Service.hxx +++ b/src/db/update/Service.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/UpdateDomain.cxx b/src/db/update/UpdateDomain.cxx index 80ad4fd22..68fcbe81c 100644 --- a/src/db/update/UpdateDomain.cxx +++ b/src/db/update/UpdateDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/UpdateDomain.hxx b/src/db/update/UpdateDomain.hxx index a6e994390..8669d24b4 100644 --- a/src/db/update/UpdateDomain.hxx +++ b/src/db/update/UpdateDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/db/update/UpdateIO.cxx b/src/db/update/UpdateIO.cxx index 9e43d1289..1a0f318c3 100644 --- a/src/db/update/UpdateIO.cxx +++ b/src/db/update/UpdateIO.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ #include <unistd.h> bool -GetInfo(Storage &storage, const char *uri_utf8, FileInfo &info) +GetInfo(Storage &storage, const char *uri_utf8, StorageFileInfo &info) { Error error; bool success = storage.GetInfo(uri_utf8, true, info, error); @@ -43,7 +43,7 @@ GetInfo(Storage &storage, const char *uri_utf8, FileInfo &info) } bool -GetInfo(StorageDirectoryReader &reader, FileInfo &info) +GetInfo(StorageDirectoryReader &reader, StorageFileInfo &info) { Error error; bool success = reader.GetInfo(true, info, error); @@ -55,7 +55,7 @@ GetInfo(StorageDirectoryReader &reader, FileInfo &info) bool DirectoryExists(Storage &storage, const Directory &directory) { - FileInfo info; + StorageFileInfo info; if (!storage.GetInfo(directory.GetPath(), true, info, IgnoreError())) return false; @@ -67,7 +67,7 @@ DirectoryExists(Storage &storage, const Directory &directory) static bool GetDirectoryChildInfo(Storage &storage, const Directory &directory, - const char *name_utf8, FileInfo &info, Error &error) + const char *name_utf8, StorageFileInfo &info, Error &error) { const auto uri_utf8 = PathTraitsUTF8::Build(directory.GetPath(), name_utf8); @@ -78,7 +78,7 @@ bool directory_child_is_regular(Storage &storage, const Directory &directory, const char *name_utf8) { - FileInfo info; + StorageFileInfo info; return GetDirectoryChildInfo(storage, directory, name_utf8, info, IgnoreError()) && info.IsRegular(); diff --git a/src/db/update/UpdateIO.hxx b/src/db/update/UpdateIO.hxx index 2dbb4ae83..0223cb62c 100644 --- a/src/db/update/UpdateIO.hxx +++ b/src/db/update/UpdateIO.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,7 @@ #include "Compiler.h" struct Directory; -struct FileInfo; +struct StorageFileInfo; class Storage; class StorageDirectoryReader; @@ -33,14 +33,14 @@ class StorageDirectoryReader; * returning them. */ bool -GetInfo(Storage &storage, const char *uri_utf8, FileInfo &info); +GetInfo(Storage &storage, const char *uri_utf8, StorageFileInfo &info); /** * Wrapper for LocalDirectoryReader::GetInfo() that logs errors * instead of returning them. */ bool -GetInfo(StorageDirectoryReader &reader, FileInfo &info); +GetInfo(StorageDirectoryReader &reader, StorageFileInfo &info); gcc_pure bool diff --git a/src/db/update/UpdateSong.cxx b/src/db/update/UpdateSong.cxx index 005bf8992..e6ed5643a 100644 --- a/src/db/update/UpdateSong.cxx +++ b/src/db/update/UpdateSong.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ inline void UpdateWalk::UpdateSongFile2(Directory &directory, const char *name, const char *suffix, - const FileInfo &info) + const StorageFileInfo &info) { db_lock(); Song *song = directory.FindSong(name); @@ -93,7 +93,7 @@ UpdateWalk::UpdateSongFile2(Directory &directory, bool UpdateWalk::UpdateSongFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info) + const StorageFileInfo &info) { if (!decoder_plugins_supports_suffix(suffix)) return false; diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index 9fdb5bbdf..3e5654a3c 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -57,17 +57,17 @@ UpdateWalk::UpdateWalk(EventLoop &_loop, DatabaseListener &_listener, { #ifndef WIN32 follow_inside_symlinks = - config_get_bool(CONF_FOLLOW_INSIDE_SYMLINKS, + config_get_bool(ConfigOption::FOLLOW_INSIDE_SYMLINKS, DEFAULT_FOLLOW_INSIDE_SYMLINKS); follow_outside_symlinks = - config_get_bool(CONF_FOLLOW_OUTSIDE_SYMLINKS, + config_get_bool(ConfigOption::FOLLOW_OUTSIDE_SYMLINKS, DEFAULT_FOLLOW_OUTSIDE_SYMLINKS); #endif } static void -directory_set_stat(Directory &dir, const FileInfo &info) +directory_set_stat(Directory &dir, const StorageFileInfo &info) { dir.inode = info.inode; dir.device = info.device; @@ -140,7 +140,7 @@ UpdateWalk::PurgeDeletedFromDirectory(Directory &directory) static bool update_directory_stat(Storage &storage, Directory &directory) { - FileInfo info; + StorageFileInfo info; if (!GetInfo(storage, directory.GetPath(), info)) return false; @@ -190,7 +190,7 @@ FindAncestorLoop(Storage &storage, Directory *parent, inline bool UpdateWalk::UpdatePlaylistFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info) + const StorageFileInfo &info) { if (!playlist_suffix_supported(suffix)) return false; @@ -206,7 +206,7 @@ UpdateWalk::UpdatePlaylistFile(Directory &directory, inline bool UpdateWalk::UpdateRegularFile(Directory &directory, - const char *name, const FileInfo &info) + const char *name, const StorageFileInfo &info) { const char *suffix = uri_get_suffix(name); if (suffix == nullptr) @@ -219,7 +219,8 @@ UpdateWalk::UpdateRegularFile(Directory &directory, void UpdateWalk::UpdateDirectoryChild(Directory &directory, - const char *name, const FileInfo &info) + const ExcludeList &exclude_list, + const char *name, const StorageFileInfo &info) { assert(strchr(name, '/') == nullptr); @@ -236,7 +237,7 @@ UpdateWalk::UpdateDirectoryChild(Directory &directory, assert(&directory == subdir->parent); - if (!UpdateDirectory(*subdir, info)) + if (!UpdateDirectory(*subdir, exclude_list, info)) editor.LockDeleteDirectory(subdir); } else { FormatDebug(update_domain, @@ -327,7 +328,9 @@ UpdateWalk::SkipSymlink(const Directory *directory, } bool -UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info) +UpdateWalk::UpdateDirectory(Directory &directory, + const ExcludeList &exclude_list, + const StorageFileInfo &info) { assert(info.IsDirectory()); @@ -340,17 +343,17 @@ UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info) return false; } - ExcludeList exclude_list; + ExcludeList child_exclude_list(exclude_list); { const auto exclude_path_fs = storage.MapChildFS(directory.GetPath(), ".mpdignore"); if (!exclude_path_fs.IsNull()) - exclude_list.LoadFile(exclude_path_fs); + child_exclude_list.LoadFile(exclude_path_fs); } - if (!exclude_list.IsEmpty()) - RemoveExcludedFromDirectory(directory, exclude_list); + if (!child_exclude_list.IsEmpty()) + RemoveExcludedFromDirectory(directory, child_exclude_list); PurgeDeletedFromDirectory(directory); @@ -361,7 +364,7 @@ UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info) { const auto name_fs = AllocatedPath::FromUTF8(name_utf8); - if (name_fs.IsNull() || exclude_list.Check(name_fs)) + if (name_fs.IsNull() || child_exclude_list.Check(name_fs)) continue; } @@ -370,13 +373,13 @@ UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info) continue; } - FileInfo info2; + StorageFileInfo info2; if (!GetInfo(*reader, info2)) { modified |= editor.DeleteNameIn(directory, name_utf8); continue; } - UpdateDirectoryChild(directory, name_utf8, info2); + UpdateDirectoryChild(directory, child_exclude_list, name_utf8, info2); } directory.mtime = info.mtime; @@ -400,7 +403,7 @@ UpdateWalk::DirectoryMakeChildChecked(Directory &parent, return directory; } - FileInfo info; + StorageFileInfo info; if (!GetInfo(storage, uri_utf8, info) || FindAncestorLoop(storage, &parent, info.inode, info.device)) return nullptr; @@ -462,13 +465,15 @@ UpdateWalk::UpdateUri(Directory &root, const char *uri) return; } - FileInfo info; + StorageFileInfo info; if (!GetInfo(storage, uri, info)) { modified |= editor.DeleteNameIn(*parent, name); return; } - UpdateDirectoryChild(*parent, name, info); + ExcludeList exclude_list; + + UpdateDirectoryChild(*parent, exclude_list, name, info); } bool @@ -480,11 +485,13 @@ UpdateWalk::Walk(Directory &root, const char *path, bool discard) if (path != nullptr && !isRootDirectory(path)) { UpdateUri(root, path); } else { - FileInfo info; + StorageFileInfo info; if (!GetInfo(storage, "", info)) return false; - UpdateDirectory(root, info); + ExcludeList exclude_list; + + UpdateDirectory(root, exclude_list, info); } return modified; diff --git a/src/db/update/Walk.hxx b/src/db/update/Walk.hxx index a4c518813..99d54ef51 100644 --- a/src/db/update/Walk.hxx +++ b/src/db/update/Walk.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #include <sys/stat.h> struct stat; -struct FileInfo; +struct StorageFileInfo; struct Directory; struct ArchivePlugin; class Storage; @@ -89,15 +89,15 @@ private: void UpdateSongFile2(Directory &directory, const char *name, const char *suffix, - const FileInfo &info); + const StorageFileInfo &info); bool UpdateSongFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info); + const StorageFileInfo &info); bool UpdateContainerFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info); + const StorageFileInfo &info); #ifdef ENABLE_ARCHIVE @@ -105,10 +105,10 @@ private: bool UpdateArchiveFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info); + const StorageFileInfo &info); void UpdateArchiveFile(Directory &directory, const char *name, - const FileInfo &info, + const StorageFileInfo &info, const ArchivePlugin &plugin); @@ -116,22 +116,26 @@ private: bool UpdateArchiveFile(gcc_unused Directory &directory, gcc_unused const char *name, gcc_unused const char *suffix, - gcc_unused const FileInfo &info) { + gcc_unused const StorageFileInfo &info) { return false; } #endif bool UpdatePlaylistFile(Directory &directory, const char *name, const char *suffix, - const FileInfo &info); + const StorageFileInfo &info); bool UpdateRegularFile(Directory &directory, - const char *name, const FileInfo &info); + const char *name, const StorageFileInfo &info); void UpdateDirectoryChild(Directory &directory, - const char *name, const FileInfo &info); + const ExcludeList &exclude_list, + const char *name, + const StorageFileInfo &info); - bool UpdateDirectory(Directory &directory, const FileInfo &info); + bool UpdateDirectory(Directory &directory, + const ExcludeList &exclude_list, + const StorageFileInfo &info); /** * Create the specified directory object if it does not exist @@ -142,7 +146,7 @@ private: * The caller must lock the database. */ Directory *MakeDirectoryIfModified(Directory &parent, const char *name, - const FileInfo &info); + const StorageFileInfo &info); Directory *DirectoryMakeChildChecked(Directory &parent, const char *uri_utf8, diff --git a/src/decoder/DecoderAPI.cxx b/src/decoder/DecoderAPI.cxx index 941d3a70d..cc08596a2 100644 --- a/src/decoder/DecoderAPI.cxx +++ b/src/decoder/DecoderAPI.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderAPI.hxx b/src/decoder/DecoderAPI.hxx index b756331d9..289298ba4 100644 --- a/src/decoder/DecoderAPI.hxx +++ b/src/decoder/DecoderAPI.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include "tag/Tag.hxx" #include "AudioFormat.hxx" #include "MixRampInfo.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "Chrono.hxx" // IWYU pragma: end_exports @@ -197,7 +197,6 @@ decoder_data(Decoder &decoder, InputStream &is, * This function is called by the decoder plugin when it has * successfully decoded a tag. * - * @param decoder the decoder object * @param is an input stream which is buffering while we are waiting * for the player * @param tag the tag to send @@ -216,9 +215,8 @@ decoder_tag(Decoder &decoder, InputStream &is, Tag &&tag) /** * Set replay gain values for the following chunks. * - * @param decoder the decoder object - * @param rgi the replay_gain_info object; may be nullptr to invalidate - * the previous replay gain values + * @param replay_gain_info the replay_gain_info object; may be nullptr + * to invalidate the previous replay gain values */ void decoder_replay_gain(Decoder &decoder, @@ -226,10 +224,6 @@ decoder_replay_gain(Decoder &decoder, /** * Store MixRamp tags. - * - * @param decoder the decoder object - * @param mixramp_start the mixramp_start tag; may be nullptr to invalidate - * @param mixramp_end the mixramp_end tag; may be nullptr to invalidate */ void decoder_mixramp(Decoder &decoder, MixRampInfo &&mix_ramp); diff --git a/src/decoder/DecoderBuffer.cxx b/src/decoder/DecoderBuffer.cxx index a8958d6fd..47c18cdd1 100644 --- a/src/decoder/DecoderBuffer.cxx +++ b/src/decoder/DecoderBuffer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderBuffer.hxx b/src/decoder/DecoderBuffer.hxx index 9cf47d915..bc7138d5b 100644 --- a/src/decoder/DecoderBuffer.hxx +++ b/src/decoder/DecoderBuffer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderCommand.hxx b/src/decoder/DecoderCommand.hxx index a00519644..88eac0181 100644 --- a/src/decoder/DecoderCommand.hxx +++ b/src/decoder/DecoderCommand.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderControl.cxx b/src/decoder/DecoderControl.cxx index c30da6214..a4fe570b2 100644 --- a/src/decoder/DecoderControl.cxx +++ b/src/decoder/DecoderControl.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderControl.hxx b/src/decoder/DecoderControl.hxx index ed2b8c538..a8e675bba 100644 --- a/src/decoder/DecoderControl.hxx +++ b/src/decoder/DecoderControl.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderError.cxx b/src/decoder/DecoderError.cxx index bd3842837..d82a4d58c 100644 --- a/src/decoder/DecoderError.cxx +++ b/src/decoder/DecoderError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderError.hxx b/src/decoder/DecoderError.hxx index 83cf98204..58aa6cb83 100644 --- a/src/decoder/DecoderError.hxx +++ b/src/decoder/DecoderError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderInternal.cxx b/src/decoder/DecoderInternal.cxx index f35878682..f0bb04125 100644 --- a/src/decoder/DecoderInternal.cxx +++ b/src/decoder/DecoderInternal.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderInternal.hxx b/src/decoder/DecoderInternal.hxx index 24b665e85..f33c80402 100644 --- a/src/decoder/DecoderInternal.hxx +++ b/src/decoder/DecoderInternal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx index cd6881ce2..e153b1398 100644 --- a/src/decoder/DecoderList.cxx +++ b/src/decoder/DecoderList.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "DecoderList.hxx" #include "DecoderPlugin.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "plugins/AudiofileDecoderPlugin.hxx" #include "plugins/PcmDecoderPlugin.hxx" #include "plugins/DsdiffDecoderPlugin.hxx" @@ -48,44 +48,42 @@ #include <string.h> const struct DecoderPlugin *const decoder_plugins[] = { -#ifdef HAVE_MAD +#ifdef ENABLE_MAD &mad_decoder_plugin, #endif -#ifdef HAVE_MPG123 +#ifdef ENABLE_MPG123 &mpg123_decoder_plugin, #endif #ifdef ENABLE_VORBIS_DECODER &vorbis_decoder_plugin, #endif -#if defined(HAVE_FLAC) +#ifdef ENABLE_FLAC &oggflac_decoder_plugin, -#endif -#ifdef HAVE_FLAC &flac_decoder_plugin, #endif -#ifdef HAVE_OPUS +#ifdef ENABLE_OPUS &opus_decoder_plugin, #endif #ifdef ENABLE_SNDFILE &sndfile_decoder_plugin, #endif -#ifdef HAVE_AUDIOFILE +#ifdef ENABLE_AUDIOFILE &audiofile_decoder_plugin, #endif #ifdef ENABLE_DSD &dsdiff_decoder_plugin, &dsf_decoder_plugin, #endif -#ifdef HAVE_FAAD +#ifdef ENABLE_FAAD &faad_decoder_plugin, #endif -#ifdef HAVE_MPCDEC +#ifdef ENABLE_MPCDEC &mpcdec_decoder_plugin, #endif -#ifdef HAVE_WAVPACK +#ifdef ENABLE_WAVPACK &wavpack_decoder_plugin, #endif -#ifdef HAVE_MODPLUG +#ifdef ENABLE_MODPLUG &modplug_decoder_plugin, #endif #ifdef ENABLE_MIKMOD_DECODER @@ -100,13 +98,13 @@ const struct DecoderPlugin *const decoder_plugins[] = { #ifdef ENABLE_FLUIDSYNTH &fluidsynth_decoder_plugin, #endif -#ifdef HAVE_ADPLUG +#ifdef ENABLE_ADPLUG &adplug_decoder_plugin, #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG &ffmpeg_decoder_plugin, #endif -#ifdef HAVE_GME +#ifdef ENABLE_GME &gme_decoder_plugin, #endif &pcm_decoder_plugin, @@ -129,12 +127,13 @@ decoder_plugin_from_name(const char *name) void decoder_plugin_init_all(void) { - struct config_param empty; + ConfigBlock empty; for (unsigned i = 0; decoder_plugins[i] != nullptr; ++i) { const DecoderPlugin &plugin = *decoder_plugins[i]; - const struct config_param *param = - config_find_block(CONF_DECODER, "plugin", plugin.name); + const auto *param = + config_find_block(ConfigBlockOption::DECODER, "plugin", + plugin.name); if (param == nullptr) param = ∅ diff --git a/src/decoder/DecoderList.hxx b/src/decoder/DecoderList.hxx index 47085d4ae..26ecc51d7 100644 --- a/src/decoder/DecoderList.hxx +++ b/src/decoder/DecoderList.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -34,10 +34,12 @@ const struct DecoderPlugin * decoder_plugin_from_name(const char *name); /* this is where we "load" all the "plugins" ;-) */ -void decoder_plugin_init_all(void); +void +decoder_plugin_init_all(); /* this is where we "unload" all the "plugins" */ -void decoder_plugin_deinit_all(void); +void +decoder_plugin_deinit_all(); template<typename F> static inline const DecoderPlugin * diff --git a/src/decoder/DecoderPlugin.cxx b/src/decoder/DecoderPlugin.cxx index a0722c348..60b1c36d4 100644 --- a/src/decoder/DecoderPlugin.cxx +++ b/src/decoder/DecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/DecoderPlugin.hxx b/src/decoder/DecoderPlugin.hxx index dbf3db9aa..7aab05615 100644 --- a/src/decoder/DecoderPlugin.hxx +++ b/src/decoder/DecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ #include "Compiler.h" -struct config_param; +struct ConfigBlock; class InputStream; struct tag_handler; class Path; @@ -44,16 +44,16 @@ struct DecoderPlugin { * @return true if the plugin was initialized successfully, * false if the plugin is not available */ - bool (*init)(const config_param ¶m); + bool (*init)(const ConfigBlock &block); /** * Deinitialize a decoder plugin which was initialized * successfully. Optional method. */ - void (*finish)(void); + void (*finish)(); /** - * Decode a stream (data read from an #input_stream object). + * Decode a stream (data read from an #InputStream object). * * Either implement this method or file_decode(). If * possible, it is recommended to implement this method, @@ -107,14 +107,13 @@ struct DecoderPlugin { /** * Initialize a decoder plugin. * - * @param param a configuration block for this plugin, or nullptr if none - * is configured + * @param block a configuration block for this plugin * @return true if the plugin was initialized successfully, false if * the plugin is not available */ - bool Init(const config_param ¶m) const { + bool Init(const ConfigBlock &block) const { return init != nullptr - ? init(param) + ? init(block) : true; } diff --git a/src/decoder/DecoderPrint.cxx b/src/decoder/DecoderPrint.cxx index 54b89c36c..6a0822596 100644 --- a/src/decoder/DecoderPrint.cxx +++ b/src/decoder/DecoderPrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,35 +21,35 @@ #include "DecoderPrint.hxx" #include "DecoderList.hxx" #include "DecoderPlugin.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" #include <functional> #include <assert.h> static void -decoder_plugin_print(Client &client, +decoder_plugin_print(Response &r, const DecoderPlugin &plugin) { const char *const*p; assert(plugin.name != nullptr); - client_printf(client, "plugin: %s\n", plugin.name); + r.Format("plugin: %s\n", plugin.name); if (plugin.suffixes != nullptr) for (p = plugin.suffixes; *p != nullptr; ++p) - client_printf(client, "suffix: %s\n", *p); + r.Format("suffix: %s\n", *p); if (plugin.mime_types != nullptr) for (p = plugin.mime_types; *p != nullptr; ++p) - client_printf(client, "mime_type: %s\n", *p); + r.Format("mime_type: %s\n", *p); } void -decoder_list_print(Client &client) +decoder_list_print(Response &r) { using namespace std::placeholders; - const auto f = std::bind(decoder_plugin_print, std::ref(client), _1); + const auto f = std::bind(decoder_plugin_print, std::ref(r), _1); decoder_plugins_for_each_enabled(f); } diff --git a/src/decoder/DecoderPrint.hxx b/src/decoder/DecoderPrint.hxx index 695bd099d..fcb995f6b 100644 --- a/src/decoder/DecoderPrint.hxx +++ b/src/decoder/DecoderPrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,9 +20,9 @@ #ifndef MPD_DECODER_PRINT_HXX #define MPD_DECODER_PRINT_HXX -class Client; +class Response; void -decoder_list_print(Client &client); +decoder_list_print(Response &r); #endif diff --git a/src/decoder/DecoderThread.cxx b/src/decoder/DecoderThread.cxx index b4362a548..2a7275a68 100644 --- a/src/decoder/DecoderThread.cxx +++ b/src/decoder/DecoderThread.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -60,14 +60,14 @@ decoder_command_finished_locked(DecoderControl &dc) } /** - * Opens the input stream with input_stream::Open(), and waits until + * Opens the input stream with InputStream::Open(), and waits until * the stream gets ready. If a decoder STOP command is received * during that, it cancels the operation (but does not close the * stream). * * Unlock the decoder before calling this function. * - * @return an input_stream on success or if #DecoderCommand::STOP is + * @return an InputStream on success or if #DecoderCommand::STOP is * received, nullptr on error */ static InputStream * @@ -267,12 +267,10 @@ static bool decoder_run_stream(Decoder &decoder, const char *uri) { DecoderControl &dc = decoder.dc; - InputStream *input_stream; - bool success; dc.Unlock(); - input_stream = decoder_input_stream_open(dc, uri); + InputStream *input_stream = decoder_input_stream_open(dc, uri); if (input_stream == nullptr) { dc.Lock(); return false; @@ -281,7 +279,7 @@ decoder_run_stream(Decoder &decoder, const char *uri) dc.Lock(); bool tried = false; - success = dc.command == DecoderCommand::STOP || + const bool success = dc.command == DecoderCommand::STOP || decoder_run_stream_locked(decoder, *input_stream, uri, tried) || /* fallback to mp3: this is needed for bastard streams @@ -385,13 +383,12 @@ decoder_run_song(DecoderControl &dc, tags on "stream" songs are just remembered from the last time we played it*/ song.IsFile() ? new Tag(song.GetTag()) : nullptr); - int ret; dc.state = DecoderState::START; decoder_command_finished_locked(dc); - ret = !path_fs.IsNull() + const int ret = !path_fs.IsNull() ? decoder_run_file(decoder, uri, path_fs) : decoder_run_stream(decoder, uri); diff --git a/src/decoder/DecoderThread.hxx b/src/decoder/DecoderThread.hxx index d5fde281c..c262a679b 100644 --- a/src/decoder/DecoderThread.hxx +++ b/src/decoder/DecoderThread.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/AdPlugDecoderPlugin.cxx b/src/decoder/plugins/AdPlugDecoderPlugin.cxx index 9cc37ade4..dd3f2b7ff 100644 --- a/src/decoder/plugins/AdPlugDecoderPlugin.cxx +++ b/src/decoder/plugins/AdPlugDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -38,14 +38,14 @@ static constexpr Domain adplug_domain("adplug"); static unsigned sample_rate; static bool -adplug_init(const config_param ¶m) +adplug_init(const ConfigBlock &block) { FormatDebug(adplug_domain, "adplug %s", CAdPlug::get_version().c_str()); Error error; - sample_rate = param.GetBlockValue("sample_rate", 48000u); + sample_rate = block.GetBlockValue("sample_rate", 48000u); if (!audio_check_sample_rate(sample_rate, error)) { LogError(error); return false; diff --git a/src/decoder/plugins/AdPlugDecoderPlugin.h b/src/decoder/plugins/AdPlugDecoderPlugin.h index 539dbbf0a..16d9e507b 100644 --- a/src/decoder/plugins/AdPlugDecoderPlugin.h +++ b/src/decoder/plugins/AdPlugDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/AudiofileDecoderPlugin.cxx b/src/decoder/plugins/AudiofileDecoderPlugin.cxx index a0ef71e49..7d59d9460 100644 --- a/src/decoder/plugins/AudiofileDecoderPlugin.cxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -43,7 +43,7 @@ audiofile_error_func(long, const char *msg) } static bool -audiofile_init(const config_param &) +audiofile_init(const ConfigBlock &) { afSetErrorHandler(audiofile_error_func); return true; diff --git a/src/decoder/plugins/AudiofileDecoderPlugin.hxx b/src/decoder/plugins/AudiofileDecoderPlugin.hxx index 61129076d..dfeda0f7d 100644 --- a/src/decoder/plugins/AudiofileDecoderPlugin.hxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/DsdLib.cxx b/src/decoder/plugins/DsdLib.cxx index d826970f7..8fefbb48c 100644 --- a/src/decoder/plugins/DsdLib.cxx +++ b/src/decoder/plugins/DsdLib.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ #include <string.h> #include <stdlib.h> -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG #include <id3tag.h> #endif @@ -48,7 +48,7 @@ DsdId::Equals(const char *s) const } /** - * Skip the #input_stream to the specified offset. + * Skip the #InputStream to the specified offset. */ bool dsdlib_skip_to(Decoder *decoder, InputStream &is, @@ -64,7 +64,7 @@ dsdlib_skip_to(Decoder *decoder, InputStream &is, } /** - * Skip some bytes from the #input_stream. + * Skip some bytes from the #InputStream. */ bool dsdlib_skip(Decoder *decoder, InputStream &is, @@ -103,31 +103,29 @@ dsdlib_valid_freq(uint32_t samplefreq) } } -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG void dsdlib_tag_id3(InputStream &is, const struct tag_handler *handler, - void *handler_ctx, int64_t tagoffset) + void *handler_ctx, offset_type tagoffset) { - assert(tagoffset >= 0); - if (tagoffset == 0 || !is.KnownSize()) return; - if (!dsdlib_skip_to(nullptr, is, tagoffset)) - return; - /* Prevent broken files causing problems */ const auto size = is.GetSize(); - const auto offset = is.GetOffset(); - if (offset >= size) + if (tagoffset >= size) return; - const id3_length_t count = size - offset; + const auto count64 = size - tagoffset; + if (count64 < 10 || count64 > 1024 * 1024) + return; - if (count < 10 || count > 1024 * 1024) + if (!dsdlib_skip_to(nullptr, is, tagoffset)) return; + const id3_length_t count = count64; + id3_byte_t *const id3_buf = new id3_byte_t[count]; if (id3_buf == nullptr) return; diff --git a/src/decoder/plugins/DsdLib.hxx b/src/decoder/plugins/DsdLib.hxx index 8295bcbf6..27cd0d37f 100644 --- a/src/decoder/plugins/DsdLib.hxx +++ b/src/decoder/plugins/DsdLib.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -81,6 +81,6 @@ dsdlib_valid_freq(uint32_t samplefreq); void dsdlib_tag_id3(InputStream &is, const struct tag_handler *handler, - void *handler_ctx, int64_t tagoffset); + void *handler_ctx, offset_type tagoffset); #endif diff --git a/src/decoder/plugins/DsdiffDecoderPlugin.cxx b/src/decoder/plugins/DsdiffDecoderPlugin.cxx index b6c79e11e..bd587e0ce 100644 --- a/src/decoder/plugins/DsdiffDecoderPlugin.cxx +++ b/src/decoder/plugins/DsdiffDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -72,9 +72,9 @@ struct DsdiffMetaData { static bool lsbitfirst; static bool -dsdiff_init(const config_param ¶m) +dsdiff_init(const ConfigBlock &block) { - lsbitfirst = param.GetBlockValue("lsbitfirst", false); + lsbitfirst = block.GetBlockValue("lsbitfirst", false); return true; } @@ -244,7 +244,7 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, /** offset for title tag */ offset_type title_offset = 0; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG offset_type id3_offset = 0; #endif @@ -269,7 +269,7 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, chunk_size = chunk_header->GetSize(); title_offset = is.GetOffset(); } -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG /* 'ID3 ' chunk, offspec. Used by sacdextract */ if (chunk_header->id.Equals("ID3 ")) { chunk_size = chunk_header->GetSize(); @@ -283,7 +283,7 @@ dsdiff_read_metadata_extra(Decoder *decoder, InputStream &is, /* done processing chunk headers, process tags if any */ -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG if (id3_offset != 0) { /* a ID3 tag has preference over the other tags, do not process other tags if we have one */ diff --git a/src/decoder/plugins/DsdiffDecoderPlugin.hxx b/src/decoder/plugins/DsdiffDecoderPlugin.hxx index 7aa36752b..503e99756 100644 --- a/src/decoder/plugins/DsdiffDecoderPlugin.hxx +++ b/src/decoder/plugins/DsdiffDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/DsfDecoderPlugin.cxx b/src/decoder/plugins/DsfDecoderPlugin.cxx index 690616d15..b111dbd51 100644 --- a/src/decoder/plugins/DsfDecoderPlugin.cxx +++ b/src/decoder/plugins/DsfDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -47,7 +47,7 @@ struct DsfMetaData { unsigned sample_rate, channels; bool bitreverse; offset_type n_blocks; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG offset_type id3_offset; #endif }; @@ -111,7 +111,7 @@ dsf_read_metadata(Decoder *decoder, InputStream &is, if (sizeof(dsf_header) != chunk_size) return false; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG const offset_type metadata_offset = dsf_header.pmeta.Read(); #endif @@ -174,7 +174,7 @@ dsf_read_metadata(Decoder *decoder, InputStream &is, metadata->n_blocks = data_size / block_size; metadata->channels = channels; metadata->sample_rate = samplefreq; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG metadata->id3_offset = metadata_offset; #endif /* check bits per sample format, determine if bitreverse is needed */ @@ -352,7 +352,7 @@ dsf_scan_stream(InputStream &is, audio_format.sample_rate); tag_handler_invoke_duration(handler, handler_ctx, songtime); -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG /* Add available tags from the ID3 tag */ dsdlib_tag_id3(is, handler, handler_ctx, metadata.id3_offset); #endif diff --git a/src/decoder/plugins/DsfDecoderPlugin.hxx b/src/decoder/plugins/DsfDecoderPlugin.hxx index 02bea0b5c..5da6217e9 100644 --- a/src/decoder/plugins/DsfDecoderPlugin.hxx +++ b/src/decoder/plugins/DsfDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FaadDecoderPlugin.cxx b/src/decoder/plugins/FaadDecoderPlugin.cxx index add23aaa4..8ea2b685c 100644 --- a/src/decoder/plugins/FaadDecoderPlugin.cxx +++ b/src/decoder/plugins/FaadDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FaadDecoderPlugin.hxx b/src/decoder/plugins/FaadDecoderPlugin.hxx index 968433e9b..3c42b4226 100644 --- a/src/decoder/plugins/FaadDecoderPlugin.hxx +++ b/src/decoder/plugins/FaadDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx index 689089107..9493fee69 100644 --- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx +++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,14 +20,25 @@ /* necessary because libavutil/common.h uses UINT64_C */ #define __STDC_CONSTANT_MACROS +#include "lib/ffmpeg/Time.hxx" #include "config.h" #include "FfmpegDecoderPlugin.hxx" #include "lib/ffmpeg/Domain.hxx" +#include "lib/ffmpeg/Error.hxx" +#include "lib/ffmpeg/LogError.hxx" +#include "lib/ffmpeg/Init.hxx" +#include "lib/ffmpeg/Buffer.hxx" #include "../DecoderAPI.hxx" #include "FfmpegMetaData.hxx" +#include "FfmpegIo.hxx" +#include "pcm/Interleave.hxx" +#include "tag/TagBuilder.hxx" #include "tag/TagHandler.hxx" +#include "tag/ReplayGain.hxx" +#include "tag/MixRamp.hxx" #include "input/InputStream.hxx" #include "CheckAudioFormat.hxx" +#include "util/ConstBuffer.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "LogV.hxx" @@ -38,7 +49,6 @@ extern "C" { #include <libavformat/avio.h> #include <libavutil/avutil.h> #include <libavutil/log.h> -#include <libavutil/mathematics.h> #if LIBAVUTIL_VERSION_MAJOR >= 53 #include <libavutil/frame.h> @@ -48,270 +58,95 @@ extern "C" { #include <assert.h> #include <string.h> -/* suppress the ffmpeg compatibility macro */ -#ifdef SampleFormat -#undef SampleFormat -#endif - -static LogLevel -import_ffmpeg_level(int level) -{ - if (level <= AV_LOG_FATAL) - return LogLevel::ERROR; - - if (level <= AV_LOG_WARNING) - return LogLevel::WARNING; - - if (level <= AV_LOG_INFO) - return LogLevel::INFO; - - return LogLevel::DEBUG; -} - -static void -mpd_ffmpeg_log_callback(gcc_unused void *ptr, int level, - const char *fmt, va_list vl) -{ - const AVClass * cls = nullptr; - - if (ptr != nullptr) - cls = *(const AVClass *const*)ptr; - - if (cls != nullptr) { - char domain[64]; - snprintf(domain, sizeof(domain), "%s/%s", - ffmpeg_domain.GetName(), cls->item_name(ptr)); - const Domain d(domain); - LogFormatV(d, import_ffmpeg_level(level), fmt, vl); - } -} - -struct AvioStream { - Decoder *const decoder; - InputStream &input; - - AVIOContext *io; - - AvioStream(Decoder *_decoder, InputStream &_input) - :decoder(_decoder), input(_input), io(nullptr) {} - - ~AvioStream() { - if (io != nullptr) { - av_free(io->buffer); - av_free(io); - } - } - - bool Open(); -}; - -static int -mpd_ffmpeg_stream_read(void *opaque, uint8_t *buf, int size) -{ - AvioStream *stream = (AvioStream *)opaque; - - return decoder_read(stream->decoder, stream->input, - (void *)buf, size); -} - -static int64_t -mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence) -{ - AvioStream *stream = (AvioStream *)opaque; - - switch (whence) { - case SEEK_SET: - break; - - case SEEK_CUR: - pos += stream->input.GetOffset(); - break; - - case SEEK_END: - if (!stream->input.KnownSize()) - return -1; - - pos += stream->input.GetSize(); - break; - - case AVSEEK_SIZE: - if (!stream->input.KnownSize()) - return -1; - - return stream->input.GetSize(); - - default: - return -1; - } - - if (!stream->input.LockSeek(pos, IgnoreError())) - return -1; - - return stream->input.GetOffset(); -} - -bool -AvioStream::Open() -{ - constexpr size_t BUFFER_SIZE = 8192; - auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE); - if (buffer == nullptr) - return false; - - io = avio_alloc_context(buffer, BUFFER_SIZE, - false, this, - mpd_ffmpeg_stream_read, nullptr, - input.IsSeekable() - ? mpd_ffmpeg_stream_seek : nullptr); - /* If avio_alloc_context() fails, who frees the buffer? The - libavformat API documentation does not specify this, it - only says that AVIOContext.buffer must be freed in the end, - however no AVIOContext exists in that failure code path. */ - return io != nullptr; -} - -/** - * API compatibility wrapper for av_open_input_stream() and - * avformat_open_input(). - */ -static int -mpd_ffmpeg_open_input(AVFormatContext **ic_ptr, - AVIOContext *pb, - const char *filename, - AVInputFormat *fmt) +static AVFormatContext * +FfmpegOpenInput(AVIOContext *pb, + const char *filename, + AVInputFormat *fmt) { AVFormatContext *context = avformat_alloc_context(); if (context == nullptr) - return AVERROR(ENOMEM); + return nullptr; context->pb = pb; - *ic_ptr = context; - return avformat_open_input(ic_ptr, filename, fmt, nullptr); + + avformat_open_input(&context, filename, fmt, nullptr); + return context; } static bool -ffmpeg_init(gcc_unused const config_param ¶m) +ffmpeg_init(gcc_unused const ConfigBlock &block) { - av_log_set_callback(mpd_ffmpeg_log_callback); - - av_register_all(); + FfmpegInit(); return true; } +gcc_pure static int -ffmpeg_find_audio_stream(const AVFormatContext *format_context) +ffmpeg_find_audio_stream(const AVFormatContext &format_context) { - for (unsigned i = 0; i < format_context->nb_streams; ++i) - if (format_context->streams[i]->codec->codec_type == + for (unsigned i = 0; i < format_context.nb_streams; ++i) + if (format_context.streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) return i; return -1; } -gcc_const -static double -time_from_ffmpeg(int64_t t, const AVRational time_base) -{ - assert(t != (int64_t)AV_NOPTS_VALUE); - - return (double)av_rescale_q(t, time_base, (AVRational){1, 1024}) - / (double)1024; -} - -template<typename Ratio> -static constexpr AVRational -RatioToAVRational() -{ - return { Ratio::num, Ratio::den }; -} - -gcc_const -static int64_t -time_to_ffmpeg(SongTime t, const AVRational time_base) -{ - return av_rescale_q(t.count(), - RatioToAVRational<SongTime::period>(), - time_base); -} - -/** - * Replace #AV_NOPTS_VALUE with the given fallback. - */ -static constexpr int64_t -timestamp_fallback(int64_t t, int64_t fallback) -{ - return gcc_likely(t != int64_t(AV_NOPTS_VALUE)) - ? t - : fallback; -} - /** * Accessor for AVStream::start_time that replaces AV_NOPTS_VALUE with * zero. We can't use AV_NOPTS_VALUE in calculations, and we simply * assume that the stream's start time is zero, which appears to be * the best way out of that situation. */ -static int64_t +static constexpr int64_t start_time_fallback(const AVStream &stream) { - return timestamp_fallback(stream.start_time, 0); -} - -static void -copy_interleave_frame2(uint8_t *dest, uint8_t **src, - unsigned nframes, unsigned nchannels, - unsigned sample_size) -{ - for (unsigned frame = 0; frame < nframes; ++frame) { - for (unsigned channel = 0; channel < nchannels; ++channel) { - memcpy(dest, src[channel] + frame * sample_size, - sample_size); - dest += sample_size; - } - } + return FfmpegTimestampFallback(stream.start_time, 0); } /** - * Copy PCM data from a AVFrame to an interleaved buffer. + * Copy PCM data from a non-empty AVFrame to an interleaved buffer. */ -static int -copy_interleave_frame(const AVCodecContext *codec_context, - const AVFrame *frame, - uint8_t **output_buffer, - uint8_t **global_buffer, int *global_buffer_size) +static ConstBuffer<void> +copy_interleave_frame(const AVCodecContext &codec_context, + const AVFrame &frame, + FfmpegBuffer &global_buffer, + Error &error) { + assert(frame.nb_samples > 0); + int plane_size; const int data_size = av_samples_get_buffer_size(&plane_size, - codec_context->channels, - frame->nb_samples, - codec_context->sample_fmt, 1); - if (data_size <= 0) - return data_size; - - if (av_sample_fmt_is_planar(codec_context->sample_fmt) && - codec_context->channels > 1) { - if(*global_buffer_size < data_size) { - av_freep(global_buffer); - - *global_buffer = (uint8_t*)av_malloc(data_size); - - if (!*global_buffer) - /* Not enough memory - shouldn't happen */ - return AVERROR(ENOMEM); - *global_buffer_size = data_size; + codec_context.channels, + frame.nb_samples, + codec_context.sample_fmt, 1); + assert(data_size != 0); + if (data_size < 0) { + SetFfmpegError(error, data_size); + return 0; + } + + void *output_buffer; + if (av_sample_fmt_is_planar(codec_context.sample_fmt) && + codec_context.channels > 1) { + output_buffer = global_buffer.GetT<uint8_t>(data_size); + if (output_buffer == nullptr) { + /* Not enough memory - shouldn't happen */ + error.SetErrno(ENOMEM); + return 0; } - *output_buffer = *global_buffer; - copy_interleave_frame2(*output_buffer, frame->extended_data, - frame->nb_samples, - codec_context->channels, - av_get_bytes_per_sample(codec_context->sample_fmt)); + + PcmInterleave(output_buffer, + ConstBuffer<const void *>((const void *const*)frame.extended_data, + codec_context.channels), + frame.nb_samples, + av_get_bytes_per_sample(codec_context.sample_fmt)); } else { - *output_buffer = frame->extended_data[0]; + output_buffer = frame.extended_data[0]; } - return data_size; + return { output_buffer, (size_t)data_size }; } /** @@ -343,81 +178,82 @@ PtsToPcmFrame(uint64_t pts, const AVStream &stream, } /** + * Decode an #AVPacket and send the resulting PCM data to the decoder + * API. + * * @param min_frame skip all data before this PCM frame number; this * is used after seeking to skip data in an AVPacket until the exact * desired time stamp has been reached */ static DecoderCommand ffmpeg_send_packet(Decoder &decoder, InputStream &is, - const AVPacket *packet, - AVCodecContext *codec_context, - const AVStream *stream, - AVFrame *frame, + AVPacket packet, + AVCodecContext &codec_context, + const AVStream &stream, + AVFrame &frame, uint64_t min_frame, size_t pcm_frame_size, - uint8_t **buffer, int *buffer_size) + FfmpegBuffer &buffer) { size_t skip_bytes = 0; - const auto pts = StreamRelativePts(*packet, *stream); + const auto pts = StreamRelativePts(packet, stream); if (pts >= 0) { if (min_frame > 0) { - auto cur_frame = PtsToPcmFrame(pts, *stream, - *codec_context); + auto cur_frame = PtsToPcmFrame(pts, stream, + codec_context); if (cur_frame < min_frame) skip_bytes = pcm_frame_size * (min_frame - cur_frame); } else decoder_timestamp(decoder, - time_from_ffmpeg(pts, stream->time_base)); + FfmpegTimeToDouble(pts, + stream.time_base)); } - AVPacket packet2 = *packet; - - uint8_t *output_buffer; + Error error; DecoderCommand cmd = DecoderCommand::NONE; - while (packet2.size > 0 && cmd == DecoderCommand::NONE) { - int audio_size = 0; + while (packet.size > 0 && cmd == DecoderCommand::NONE) { int got_frame = 0; - int len = avcodec_decode_audio4(codec_context, - frame, &got_frame, - &packet2); - if (len >= 0 && got_frame) { - audio_size = copy_interleave_frame(codec_context, - frame, - &output_buffer, - buffer, buffer_size); - if (audio_size < 0) - len = audio_size; - } - + int len = avcodec_decode_audio4(&codec_context, + &frame, &got_frame, + &packet); if (len < 0) { /* if error, we skip the frame */ - LogDefault(ffmpeg_domain, - "decoding failed, frame skipped"); + LogFfmpegError(len, "decoding failed, frame skipped"); break; } - packet2.data += len; - packet2.size -= len; + packet.data += len; + packet.size -= len; - if (audio_size <= 0) + if (!got_frame || frame.nb_samples <= 0) continue; - const uint8_t *data = output_buffer; + auto output_buffer = + copy_interleave_frame(codec_context, frame, + buffer, error); + if (output_buffer.IsNull()) { + /* this must be a serious error, + e.g. OOM */ + LogError(error); + return DecoderCommand::STOP; + } + if (skip_bytes > 0) { - if (skip_bytes >= size_t(audio_size)) { - skip_bytes -= audio_size; + if (skip_bytes >= output_buffer.size) { + skip_bytes -= output_buffer.size; continue; } - data += skip_bytes; - audio_size -= skip_bytes; + output_buffer.data = + (const uint8_t *)output_buffer.data + skip_bytes; + output_buffer.size -= skip_bytes; skip_bytes = 0; } cmd = decoder_data(decoder, is, - data, audio_size, - codec_context->bit_rate / 1000); + output_buffer.data, output_buffer.size, + codec_context.bit_rate / 1000); } return cmd; } @@ -460,10 +296,8 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt) static AVInputFormat * ffmpeg_probe(Decoder *decoder, InputStream &is) { - enum { - BUFFER_SIZE = 16384, - PADDING = 16, - }; + constexpr size_t BUFFER_SIZE = 16384; + constexpr size_t PADDING = 16; unsigned char buffer[BUFFER_SIZE]; size_t nbytes = decoder_read(decoder, is, buffer, BUFFER_SIZE); @@ -503,85 +337,163 @@ ffmpeg_probe(Decoder *decoder, InputStream &is) } static void -ffmpeg_decode(Decoder &decoder, InputStream &input) +FfmpegParseMetaData(AVDictionary &dict, ReplayGainInfo &rg, MixRampInfo &mr) { - AVInputFormat *input_format = ffmpeg_probe(&decoder, input); - if (input_format == nullptr) - return; + AVDictionaryEntry *i = nullptr; - FormatDebug(ffmpeg_domain, "detected input format '%s' (%s)", - input_format->name, input_format->long_name); + while ((i = av_dict_get(&dict, "", i, + AV_DICT_IGNORE_SUFFIX)) != nullptr) { + const char *name = i->key; + const char *value = i->value; - AvioStream stream(&decoder, input); - if (!stream.Open()) { - LogError(ffmpeg_domain, "Failed to open stream"); - return; + if (!ParseReplayGainTag(rg, name, value)) + ParseMixRampTag(mr, name, value); } +} - //ffmpeg works with ours "fileops" helper - AVFormatContext *format_context = nullptr; - if (mpd_ffmpeg_open_input(&format_context, stream.io, - input.GetURI(), - input_format) != 0) { - LogError(ffmpeg_domain, "Open failed"); +static void +FfmpegParseMetaData(const AVStream &stream, + ReplayGainInfo &rg, MixRampInfo &mr) +{ + FfmpegParseMetaData(*stream.metadata, rg, mr); +} + +static void +FfmpegParseMetaData(const AVFormatContext &format_context, int audio_stream, + ReplayGainInfo &rg, MixRampInfo &mr) +{ + assert(audio_stream >= 0); + + FfmpegParseMetaData(*format_context.metadata, rg, mr); + FfmpegParseMetaData(*format_context.streams[audio_stream], + rg, mr); +} + +static void +FfmpegParseMetaData(Decoder &decoder, + const AVFormatContext &format_context, int audio_stream) +{ + ReplayGainInfo rg; + rg.Clear(); + + MixRampInfo mr; + mr.Clear(); + + FfmpegParseMetaData(format_context, audio_stream, rg, mr); + + if (rg.IsDefined()) + decoder_replay_gain(decoder, &rg); + + if (mr.IsDefined()) + decoder_mixramp(decoder, std::move(mr)); +} + +static void +FfmpegScanMetadata(const AVStream &stream, + const tag_handler &handler, void *handler_ctx) +{ + FfmpegScanDictionary(stream.metadata, &handler, handler_ctx); +} + +static void +FfmpegScanMetadata(const AVFormatContext &format_context, int audio_stream, + const tag_handler &handler, void *handler_ctx) +{ + assert(audio_stream >= 0); + + FfmpegScanDictionary(format_context.metadata, &handler, handler_ctx); + FfmpegScanMetadata(*format_context.streams[audio_stream], + handler, handler_ctx); +} + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(56, 1, 0) + +static void +FfmpegScanTag(const AVFormatContext &format_context, int audio_stream, + TagBuilder &tag) +{ + FfmpegScanMetadata(format_context, audio_stream, + full_tag_handler, &tag); +} + +/** + * Check if a new stream tag was received and pass it to + * decoder_tag(). + */ +static void +FfmpegCheckTag(Decoder &decoder, InputStream &is, + AVFormatContext &format_context, int audio_stream) +{ + AVStream &stream = *format_context.streams[audio_stream]; + if ((stream.event_flags & AVSTREAM_EVENT_FLAG_METADATA_UPDATED) == 0) + /* no new metadata */ return; - } + /* clear the flag */ + stream.event_flags &= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED; + + TagBuilder tag; + FfmpegScanTag(format_context, audio_stream, tag); + if (!tag.IsEmpty()) + decoder_tag(decoder, is, tag.Commit()); +} + +#endif + +static void +FfmpegDecode(Decoder &decoder, InputStream &input, + AVFormatContext &format_context) +{ const int find_result = - avformat_find_stream_info(format_context, nullptr); + avformat_find_stream_info(&format_context, nullptr); if (find_result < 0) { LogError(ffmpeg_domain, "Couldn't find stream info"); - avformat_close_input(&format_context); return; } int audio_stream = ffmpeg_find_audio_stream(format_context); if (audio_stream == -1) { LogError(ffmpeg_domain, "No audio stream inside"); - avformat_close_input(&format_context); return; } - AVStream *av_stream = format_context->streams[audio_stream]; + AVStream &av_stream = *format_context.streams[audio_stream]; - AVCodecContext *codec_context = av_stream->codec; + AVCodecContext &codec_context = *av_stream.codec; #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 25, 0) const AVCodecDescriptor *codec_descriptor = - avcodec_descriptor_get(codec_context->codec_id); + avcodec_descriptor_get(codec_context.codec_id); if (codec_descriptor != nullptr) FormatDebug(ffmpeg_domain, "codec '%s'", codec_descriptor->name); #else - if (codec_context->codec_name[0] != 0) + if (codec_context.codec_name[0] != 0) FormatDebug(ffmpeg_domain, "codec '%s'", - codec_context->codec_name); + codec_context.codec_name); #endif - AVCodec *codec = avcodec_find_decoder(codec_context->codec_id); + AVCodec *codec = avcodec_find_decoder(codec_context.codec_id); if (!codec) { LogError(ffmpeg_domain, "Unsupported audio codec"); - avformat_close_input(&format_context); return; } const SampleFormat sample_format = - ffmpeg_sample_format(codec_context->sample_fmt); + ffmpeg_sample_format(codec_context.sample_fmt); if (sample_format == SampleFormat::UNDEFINED) { // (error message already done by ffmpeg_sample_format()) - avformat_close_input(&format_context); return; } Error error; AudioFormat audio_format; if (!audio_format_init_checked(audio_format, - codec_context->sample_rate, + codec_context.sample_rate, sample_format, - codec_context->channels, error)) { + codec_context.channels, error)) { LogError(error); - avformat_close_input(&format_context); return; } @@ -590,22 +502,20 @@ ffmpeg_decode(Decoder &decoder, InputStream &input) values into AVCodecContext.channels - a change that will be reverted later by avcodec_decode_audio3() */ - const int open_result = avcodec_open2(codec_context, codec, nullptr); + const int open_result = avcodec_open2(&codec_context, codec, nullptr); if (open_result < 0) { LogError(ffmpeg_domain, "Could not open codec"); - avformat_close_input(&format_context); return; } const SignedSongTime total_time = - format_context->duration != (int64_t)AV_NOPTS_VALUE - ? SignedSongTime::FromScale<uint64_t>(format_context->duration, - AV_TIME_BASE) - : SignedSongTime::Negative(); + FromFfmpegTimeChecked(av_stream.duration, av_stream.time_base); decoder_initialized(decoder, audio_format, input.IsSeekable(), total_time); + FfmpegParseMetaData(decoder, format_context, audio_stream); + #if LIBAVUTIL_VERSION_MAJOR >= 53 AVFrame *frame = av_frame_alloc(); #else @@ -613,70 +523,119 @@ ffmpeg_decode(Decoder &decoder, InputStream &input) #endif if (!frame) { LogError(ffmpeg_domain, "Could not allocate frame"); - avformat_close_input(&format_context); return; } - uint8_t *interleaved_buffer = nullptr; - int interleaved_buffer_size = 0; + FfmpegBuffer interleaved_buffer; uint64_t min_frame = 0; - DecoderCommand cmd; - do { - AVPacket packet; - if (av_read_frame(format_context, &packet) < 0) - /* end of file */ - break; - - if (packet.stream_index == audio_stream) { - cmd = ffmpeg_send_packet(decoder, input, - &packet, codec_context, - av_stream, - frame, - min_frame, audio_format.GetFrameSize(), - &interleaved_buffer, &interleaved_buffer_size); - min_frame = 0; - } else - cmd = decoder_get_command(decoder); - - av_free_packet(&packet); - + DecoderCommand cmd = decoder_get_command(decoder); + while (cmd != DecoderCommand::STOP) { if (cmd == DecoderCommand::SEEK) { int64_t where = - time_to_ffmpeg(decoder_seek_time(decoder), - av_stream->time_base) + - start_time_fallback(*av_stream); + ToFfmpegTime(decoder_seek_time(decoder), + av_stream.time_base) + + start_time_fallback(av_stream); /* AVSEEK_FLAG_BACKWARD asks FFmpeg to seek to the packet boundary before the seek time stamp, not after */ - - if (av_seek_frame(format_context, audio_stream, where, + if (av_seek_frame(&format_context, audio_stream, where, AVSEEK_FLAG_ANY|AVSEEK_FLAG_BACKWARD) < 0) decoder_seek_error(decoder); else { - avcodec_flush_buffers(codec_context); + avcodec_flush_buffers(&codec_context); min_frame = decoder_seek_where_frame(decoder); decoder_command_finished(decoder); } } - } while (cmd != DecoderCommand::STOP); + + AVPacket packet; + if (av_read_frame(&format_context, &packet) < 0) + /* end of file */ + break; + +#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(56, 1, 0) + FfmpegCheckTag(decoder, input, format_context, audio_stream); +#endif + + if (packet.stream_index == audio_stream) { + cmd = ffmpeg_send_packet(decoder, input, + packet, codec_context, + av_stream, + *frame, + min_frame, audio_format.GetFrameSize(), + interleaved_buffer); + min_frame = 0; + } else + cmd = decoder_get_command(decoder); + + av_free_packet(&packet); + } #if LIBAVUTIL_VERSION_MAJOR >= 53 av_frame_free(&frame); #elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0) avcodec_free_frame(&frame); #else - av_freep(&frame); + av_free(frame); #endif - av_freep(&interleaved_buffer); - avcodec_close(codec_context); + avcodec_close(&codec_context); +} + +static void +ffmpeg_decode(Decoder &decoder, InputStream &input) +{ + AVInputFormat *input_format = ffmpeg_probe(&decoder, input); + if (input_format == nullptr) + return; + + FormatDebug(ffmpeg_domain, "detected input format '%s' (%s)", + input_format->name, input_format->long_name); + + AvioStream stream(&decoder, input); + if (!stream.Open()) { + LogError(ffmpeg_domain, "Failed to open stream"); + return; + } + + AVFormatContext *format_context = + FfmpegOpenInput(stream.io, input.GetURI(), input_format); + if (format_context == nullptr) { + LogError(ffmpeg_domain, "Open failed"); + return; + } + + FfmpegDecode(decoder, input, *format_context); avformat_close_input(&format_context); } -//no tag reading in ffmpeg, check if playable +static bool +FfmpegScanStream(AVFormatContext &format_context, + const struct tag_handler &handler, void *handler_ctx) +{ + const int find_result = + avformat_find_stream_info(&format_context, nullptr); + if (find_result < 0) + return false; + + const int audio_stream = ffmpeg_find_audio_stream(format_context); + if (audio_stream < 0) + return false; + + const AVStream &stream = *format_context.streams[audio_stream]; + if (stream.duration != (int64_t)AV_NOPTS_VALUE) + tag_handler_invoke_duration(&handler, handler_ctx, + FromFfmpegTime(stream.duration, + stream.time_base)); + + FfmpegScanMetadata(format_context, audio_stream, handler, handler_ctx); + + return true; +} + static bool ffmpeg_scan_stream(InputStream &is, const struct tag_handler *handler, void *handler_ctx) @@ -689,33 +648,14 @@ ffmpeg_scan_stream(InputStream &is, if (!stream.Open()) return false; - AVFormatContext *f = nullptr; - if (mpd_ffmpeg_open_input(&f, stream.io, is.GetURI(), - input_format) != 0) - return false; - - const int find_result = - avformat_find_stream_info(f, nullptr); - if (find_result < 0) { - avformat_close_input(&f); + AVFormatContext *f = + FfmpegOpenInput(stream.io, is.GetURI(), input_format); + if (f == nullptr) return false; - } - - if (f->duration != (int64_t)AV_NOPTS_VALUE) { - const auto duration = - SongTime::FromScale<uint64_t>(f->duration, - AV_TIME_BASE); - tag_handler_invoke_duration(handler, handler_ctx, duration); - } - - ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx); - int idx = ffmpeg_find_audio_stream(f); - if (idx >= 0) - ffmpeg_scan_dictionary(f->streams[idx]->metadata, - handler, handler_ctx); + bool result = FfmpegScanStream(*f, *handler, handler_ctx); avformat_close_input(&f); - return true; + return result; } /** diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.hxx b/src/decoder/plugins/FfmpegDecoderPlugin.hxx index 0a3e78e4b..5f2710d10 100644 --- a/src/decoder/plugins/FfmpegDecoderPlugin.hxx +++ b/src/decoder/plugins/FfmpegDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FfmpegIo.cxx b/src/decoder/plugins/FfmpegIo.cxx new file mode 100644 index 000000000..08fddffa5 --- /dev/null +++ b/src/decoder/plugins/FfmpegIo.cxx @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2003-2015 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. + */ + +/* necessary because libavutil/common.h uses UINT64_C */ +#define __STDC_CONSTANT_MACROS + +#include "config.h" +#include "FfmpegIo.hxx" +#include "../DecoderAPI.hxx" +#include "input/InputStream.hxx" +#include "util/Error.hxx" + +AvioStream::~AvioStream() +{ + if (io != nullptr) { + av_free(io->buffer); + av_free(io); + } +} + +inline int +AvioStream::Read(void *dest, int size) +{ + return decoder_read(decoder, input, dest, size); +} + +inline int64_t +AvioStream::Seek(int64_t pos, int whence) +{ + switch (whence) { + case SEEK_SET: + break; + + case SEEK_CUR: + pos += input.GetOffset(); + break; + + case SEEK_END: + if (!input.KnownSize()) + return -1; + + pos += input.GetSize(); + break; + + case AVSEEK_SIZE: + if (!input.KnownSize()) + return -1; + + return input.GetSize(); + + default: + return -1; + } + + if (!input.LockSeek(pos, IgnoreError())) + return -1; + + return input.GetOffset(); +} + +int +AvioStream::_Read(void *opaque, uint8_t *buf, int size) +{ + AvioStream &stream = *(AvioStream *)opaque; + + return stream.Read(buf, size); +} + +int64_t +AvioStream::_Seek(void *opaque, int64_t pos, int whence) +{ + AvioStream &stream = *(AvioStream *)opaque; + + return stream.Seek(pos, whence); +} + +bool +AvioStream::Open() +{ + constexpr size_t BUFFER_SIZE = 8192; + auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE); + if (buffer == nullptr) + return false; + + io = avio_alloc_context(buffer, BUFFER_SIZE, + false, this, + _Read, nullptr, + input.IsSeekable() ? _Seek : nullptr); + /* If avio_alloc_context() fails, who frees the buffer? The + libavformat API documentation does not specify this, it + only says that AVIOContext.buffer must be freed in the end, + however no AVIOContext exists in that failure code path. */ + return io != nullptr; +} diff --git a/src/decoder/plugins/FfmpegIo.hxx b/src/decoder/plugins/FfmpegIo.hxx new file mode 100644 index 000000000..2deb7fd38 --- /dev/null +++ b/src/decoder/plugins/FfmpegIo.hxx @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2003-2015 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_FFMPEG_IO_HXX +#define MPD_FFMPEG_IO_HXX + +#include "check.h" + +extern "C" { +#include "libavformat/avio.h" +} + +#include <stdint.h> + +class InputStream; +struct Decoder; + +struct AvioStream { + Decoder *const decoder; + InputStream &input; + + AVIOContext *io; + + AvioStream(Decoder *_decoder, InputStream &_input) + :decoder(_decoder), input(_input), io(nullptr) {} + + ~AvioStream(); + + bool Open(); + +private: + int Read(void *buffer, int size); + int64_t Seek(int64_t pos, int whence); + + static int _Read(void *opaque, uint8_t *buf, int size); + static int64_t _Seek(void *opaque, int64_t pos, int whence); +}; + +#endif diff --git a/src/decoder/plugins/FfmpegMetaData.cxx b/src/decoder/plugins/FfmpegMetaData.cxx index a39466945..102d5669e 100644 --- a/src/decoder/plugins/FfmpegMetaData.cxx +++ b/src/decoder/plugins/FfmpegMetaData.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,11 @@ #include "tag/TagTable.hxx" #include "tag/TagHandler.hxx" -static const struct tag_table ffmpeg_tags[] = { +extern "C" { +#include <libavutil/dict.h> +} + +static constexpr struct tag_table ffmpeg_tags[] = { { "year", TAG_DATE }, { "author-sort", TAG_ARTIST_SORT }, { "album_artist", TAG_ALBUM_ARTIST }, @@ -36,9 +40,9 @@ static const struct tag_table ffmpeg_tags[] = { }; static void -ffmpeg_copy_metadata(TagType type, - AVDictionary *m, const char *name, - const struct tag_handler *handler, void *handler_ctx) +FfmpegScanTag(TagType type, + AVDictionary *m, const char *name, + const struct tag_handler *handler, void *handler_ctx) { AVDictionaryEntry *mt = nullptr; @@ -48,8 +52,8 @@ ffmpeg_copy_metadata(TagType type, } static void -ffmpeg_scan_pairs(AVDictionary *dict, - const struct tag_handler *handler, void *handler_ctx) +FfmpegScanPairs(AVDictionary *dict, + const struct tag_handler *handler, void *handler_ctx) { AVDictionaryEntry *i = nullptr; @@ -59,18 +63,20 @@ ffmpeg_scan_pairs(AVDictionary *dict, } void -ffmpeg_scan_dictionary(AVDictionary *dict, - const struct tag_handler *handler, void *handler_ctx) +FfmpegScanDictionary(AVDictionary *dict, + const struct tag_handler *handler, void *handler_ctx) { - for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) - ffmpeg_copy_metadata(TagType(i), dict, tag_item_names[i], - handler, handler_ctx); + if (handler->tag != nullptr) { + for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) + FfmpegScanTag(TagType(i), dict, tag_item_names[i], + handler, handler_ctx); - for (const struct tag_table *i = ffmpeg_tags; - i->name != nullptr; ++i) - ffmpeg_copy_metadata(i->type, dict, i->name, - handler, handler_ctx); + for (const struct tag_table *i = ffmpeg_tags; + i->name != nullptr; ++i) + FfmpegScanTag(i->type, dict, i->name, + handler, handler_ctx); + } if (handler->pair != nullptr) - ffmpeg_scan_pairs(dict, handler, handler_ctx); + FfmpegScanPairs(dict, handler, handler_ctx); } diff --git a/src/decoder/plugins/FfmpegMetaData.hxx b/src/decoder/plugins/FfmpegMetaData.hxx index 5eb41db68..bdf5dd613 100644 --- a/src/decoder/plugins/FfmpegMetaData.hxx +++ b/src/decoder/plugins/FfmpegMetaData.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,19 +20,11 @@ #ifndef MPD_FFMPEG_METADATA_HXX #define MPD_FFMPEG_METADATA_HXX -extern "C" { -#include <libavutil/dict.h> -} - -/* suppress the ffmpeg compatibility macro */ -#ifdef SampleFormat -#undef SampleFormat -#endif - +struct AVDictionary; struct tag_handler; void -ffmpeg_scan_dictionary(AVDictionary *dict, - const tag_handler *handler, void *handler_ctx); +FfmpegScanDictionary(AVDictionary *dict, + const tag_handler *handler, void *handler_ctx); #endif diff --git a/src/decoder/plugins/FlacCommon.cxx b/src/decoder/plugins/FlacCommon.cxx index e86f85569..e68871b1d 100644 --- a/src/decoder/plugins/FlacCommon.cxx +++ b/src/decoder/plugins/FlacCommon.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacCommon.hxx b/src/decoder/plugins/FlacCommon.hxx index 34ce0a3fc..e6c800cb9 100644 --- a/src/decoder/plugins/FlacCommon.hxx +++ b/src/decoder/plugins/FlacCommon.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacDecoderPlugin.cxx b/src/decoder/plugins/FlacDecoderPlugin.cxx index eea813401..410af9267 100644 --- a/src/decoder/plugins/FlacDecoderPlugin.cxx +++ b/src/decoder/plugins/FlacDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "FlacMetadata.hxx" #include "OggCodec.hxx" #include "fs/Path.hxx" +#include "fs/NarrowPath.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -84,7 +85,7 @@ flac_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { FlacMetadataChain chain; - if (!chain.Read(path_fs.c_str())) { + if (!chain.Read(NarrowPath(path_fs))) { FormatDebug(flac_domain, "Failed to read FLAC tags: %s", chain.GetStatusString()); @@ -291,7 +292,7 @@ flac_decode(Decoder &decoder, InputStream &input_stream) } static bool -oggflac_init(gcc_unused const config_param ¶m) +oggflac_init(gcc_unused const ConfigBlock &block) { return !!FLAC_API_SUPPORTS_OGG_FLAC; } @@ -301,7 +302,7 @@ oggflac_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { FlacMetadataChain chain; - if (!chain.ReadOgg(path_fs.c_str())) { + if (!chain.ReadOgg(NarrowPath(path_fs))) { FormatDebug(flac_domain, "Failed to read OggFLAC tags: %s", chain.GetStatusString()); diff --git a/src/decoder/plugins/FlacDecoderPlugin.h b/src/decoder/plugins/FlacDecoderPlugin.h index fcdecf869..fa92ff32b 100644 --- a/src/decoder/plugins/FlacDecoderPlugin.h +++ b/src/decoder/plugins/FlacDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacDomain.cxx b/src/decoder/plugins/FlacDomain.cxx index fc5cc5498..591da3ee2 100644 --- a/src/decoder/plugins/FlacDomain.cxx +++ b/src/decoder/plugins/FlacDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacDomain.hxx b/src/decoder/plugins/FlacDomain.hxx index a06c6c6b4..a27b91d40 100644 --- a/src/decoder/plugins/FlacDomain.hxx +++ b/src/decoder/plugins/FlacDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacIOHandle.cxx b/src/decoder/plugins/FlacIOHandle.cxx index 0dd895798..b85efe6c8 100644 --- a/src/decoder/plugins/FlacIOHandle.cxx +++ b/src/decoder/plugins/FlacIOHandle.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacIOHandle.hxx b/src/decoder/plugins/FlacIOHandle.hxx index 90acc66af..e840528b1 100644 --- a/src/decoder/plugins/FlacIOHandle.hxx +++ b/src/decoder/plugins/FlacIOHandle.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacInput.cxx b/src/decoder/plugins/FlacInput.cxx index 5b4c3104d..91f54b807 100644 --- a/src/decoder/plugins/FlacInput.cxx +++ b/src/decoder/plugins/FlacInput.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacInput.hxx b/src/decoder/plugins/FlacInput.hxx index 427abccb4..f55b2ebbd 100644 --- a/src/decoder/plugins/FlacInput.hxx +++ b/src/decoder/plugins/FlacInput.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacMetadata.cxx b/src/decoder/plugins/FlacMetadata.cxx index 03e276dce..14a9fe294 100644 --- a/src/decoder/plugins/FlacMetadata.cxx +++ b/src/decoder/plugins/FlacMetadata.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -30,7 +30,7 @@ #include "tag/MixRamp.hxx" #include "ReplayGainInfo.hxx" #include "util/ASCII.hxx" -#include "util/SplitString.hxx" +#include "util/DivideString.hxx" bool flac_parse_replay_gain(ReplayGainInfo &rgi, @@ -97,7 +97,7 @@ flac_scan_comment(const FLAC__StreamMetadata_VorbisComment_Entry *entry, { if (handler->pair != nullptr) { const char *comment = (const char *)entry->entry; - const SplitString split(comment, '='); + const DivideString split(comment, '='); if (split.IsDefined() && !split.IsEmpty()) tag_handler_invoke_pair(handler, handler_ctx, split.GetFirst(), diff --git a/src/decoder/plugins/FlacMetadata.hxx b/src/decoder/plugins/FlacMetadata.hxx index d791fa34e..4805f8042 100644 --- a/src/decoder/plugins/FlacMetadata.hxx +++ b/src/decoder/plugins/FlacMetadata.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacPcm.cxx b/src/decoder/plugins/FlacPcm.cxx index 311500f26..7ba4a6ad3 100644 --- a/src/decoder/plugins/FlacPcm.cxx +++ b/src/decoder/plugins/FlacPcm.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FlacPcm.hxx b/src/decoder/plugins/FlacPcm.hxx index 30c318725..51233a689 100644 --- a/src/decoder/plugins/FlacPcm.hxx +++ b/src/decoder/plugins/FlacPcm.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/FluidsynthDecoderPlugin.cxx b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx index f19ac5bf4..2b4967b95 100644 --- a/src/decoder/plugins/FluidsynthDecoderPlugin.cxx +++ b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -35,7 +35,7 @@ static unsigned sample_rate; static const char *soundfont_path; /** - * Convert a fluidsynth log level to a GLib log level. + * Convert a fluidsynth log level to a MPD log level. */ static LogLevel fluidsynth_level_to_mpd(enum fluid_log_level level) @@ -61,7 +61,7 @@ fluidsynth_level_to_mpd(enum fluid_log_level level) } /** - * The fluidsynth logging callback. It forwards messages to the GLib + * The fluidsynth logging callback. It forwards messages to the MPD * logging library. */ static void @@ -73,17 +73,17 @@ fluidsynth_mpd_log_function(int level, char *message, gcc_unused void *data) } static bool -fluidsynth_init(const config_param ¶m) +fluidsynth_init(const ConfigBlock &block) { Error error; - sample_rate = param.GetBlockValue("sample_rate", 48000u); + sample_rate = block.GetBlockValue("sample_rate", 48000u); if (!audio_check_sample_rate(sample_rate, error)) { LogError(error); return false; } - soundfont_path = param.GetBlockValue("soundfont", + soundfont_path = block.GetBlockValue("soundfont", "/usr/share/sounds/sf2/FluidR3_GM.sf2"); fluid_set_log_function(LAST_LOG_LEVEL, diff --git a/src/decoder/plugins/FluidsynthDecoderPlugin.hxx b/src/decoder/plugins/FluidsynthDecoderPlugin.hxx index cd8ec2d62..9845fd72e 100644 --- a/src/decoder/plugins/FluidsynthDecoderPlugin.hxx +++ b/src/decoder/plugins/FluidsynthDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx index b47e9ea66..cdaa68dcf 100644 --- a/src/decoder/plugins/GmeDecoderPlugin.cxx +++ b/src/decoder/plugins/GmeDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,9 +20,11 @@ #include "config.h" #include "GmeDecoderPlugin.hxx" #include "../DecoderAPI.hxx" +#include "config/Block.cxx" #include "CheckAudioFormat.hxx" #include "tag/TagHandler.hxx" #include "fs/Path.hxx" +#include "fs/AllocatedPath.hxx" #include "util/Alloc.hxx" #include "util/FormatString.hxx" #include "util/UriUtil.hxx" @@ -30,7 +32,6 @@ #include "util/Domain.hxx" #include "Log.hxx" -#include <glib.h> #include <assert.h> #include <stdlib.h> #include <string.h> @@ -47,65 +48,59 @@ static constexpr unsigned GME_BUFFER_FRAMES = 2048; static constexpr unsigned GME_BUFFER_SAMPLES = GME_BUFFER_FRAMES * GME_CHANNELS; -/** - * returns the file path stripped of any /tune_xxx.* subtune - * suffix - */ -static char * -get_container_name(Path path_fs) +struct GmeContainerPath { + AllocatedPath path; + unsigned track; +}; + +#if GME_VERSION >= 0x000600 +static int gme_accuracy; +#endif + +static bool +gme_plugin_init(gcc_unused const ConfigBlock &block) { - const char *subtune_suffix = uri_get_suffix(path_fs.c_str()); - char *path_container = xstrdup(path_fs.c_str()); - - char pat[64]; - snprintf(pat, sizeof(pat), "%s%s", - "*/" SUBTUNE_PREFIX "???.", - subtune_suffix); - GPatternSpec *path_with_subtune = g_pattern_spec_new(pat); - if (!g_pattern_match(path_with_subtune, - strlen(path_container), path_container, nullptr)) { - g_pattern_spec_free(path_with_subtune); - return path_container; - } +#if GME_VERSION >= 0x000600 + auto accuracy = block.GetBlockParam("accuracy"); + gme_accuracy = accuracy != nullptr + ? (int)accuracy->GetBoolValue() + : -1; +#endif - char *ptr = g_strrstr(path_container, "/" SUBTUNE_PREFIX); - if (ptr != nullptr) - *ptr='\0'; + return true; +} + +gcc_pure +static unsigned +ParseSubtuneName(const char *base) +{ + if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0) + return 0; + + base += sizeof(SUBTUNE_PREFIX) - 1; - g_pattern_spec_free(path_with_subtune); - return path_container; + char *endptr; + auto track = strtoul(base, &endptr, 10); + if (endptr == base || *endptr != '.') + return 0; + + return track; } /** - * returns tune number from file.nsf/tune_xxx.* style path or 0 if no subtune - * is appended. + * returns the file path stripped of any /tune_xxx.* subtune suffix + * and the track number (or 0 if no "tune_xxx" suffix is present). */ -static int -get_song_num(Path path_fs) +static GmeContainerPath +ParseContainerPath(Path path_fs) { - const char *subtune_suffix = uri_get_suffix(path_fs.c_str()); + const Path base = path_fs.GetBase(); + unsigned track; + if (base.IsNull() || + (track = ParseSubtuneName(base.c_str())) < 1) + return { AllocatedPath(path_fs), 0 }; - char pat[64]; - snprintf(pat, sizeof(pat), "%s%s", - "*/" SUBTUNE_PREFIX "???.", - subtune_suffix); - GPatternSpec *path_with_subtune = g_pattern_spec_new(pat); - - if (g_pattern_match(path_with_subtune, - path_fs.length(), path_fs.data(), nullptr)) { - char *sub = g_strrstr(path_fs.c_str(), "/" SUBTUNE_PREFIX); - g_pattern_spec_free(path_with_subtune); - if (!sub) - return 0; - - sub += strlen("/" SUBTUNE_PREFIX); - int song_num = strtol(sub, nullptr, 10); - - return song_num - 1; - } else { - g_pattern_spec_free(path_with_subtune); - return 0; - } + return { path_fs.GetDirectoryName(), track - 1 }; } static char * @@ -136,20 +131,26 @@ gme_container_scan(Path path_fs, const unsigned int tnum) static void gme_file_decode(Decoder &decoder, Path path_fs) { - char *path_container = get_container_name(path_fs); + const auto container = ParseContainerPath(path_fs); Music_Emu *emu; const char *gme_err = - gme_open_file(path_container, &emu, GME_SAMPLE_RATE); - free(path_container); + gme_open_file(container.path.c_str(), &emu, GME_SAMPLE_RATE); if (gme_err != nullptr) { LogWarning(gme_domain, gme_err); return; } + FormatDebug(gme_domain, "emulator type '%s'\n", + gme_type_system(gme_type(emu))); + +#if GME_VERSION >= 0x000600 + if (gme_accuracy >= 0) + gme_enable_accuracy(emu, gme_accuracy); +#endif + gme_info_t *ti; - const int song_num = get_song_num(path_fs); - gme_err = gme_track_info(emu, &ti, song_num); + gme_err = gme_track_info(emu, &ti, container.track); if (gme_err != nullptr) { LogWarning(gme_domain, gme_err); gme_delete(emu); @@ -177,7 +178,7 @@ gme_file_decode(Decoder &decoder, Path path_fs) decoder_initialized(decoder, audio_format, true, song_len); - gme_err = gme_start_track(emu, song_num); + gme_err = gme_start_track(emu, container.track); if (gme_err != nullptr) LogWarning(gme_domain, gme_err); @@ -212,72 +213,85 @@ gme_file_decode(Decoder &decoder, Path path_fs) gme_delete(emu); } -static bool -gme_scan_file(Path path_fs, - const struct tag_handler *handler, void *handler_ctx) +static void +ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count, + const struct tag_handler *handler, void *handler_ctx) { - char *path_container = get_container_name(path_fs); - - Music_Emu *emu; - const char *gme_err = - gme_open_file(path_container, &emu, GME_SAMPLE_RATE); - free(path_container); - if (gme_err != nullptr) { - LogWarning(gme_domain, gme_err); - return false; - } - - const int song_num = get_song_num(path_fs); - - gme_info_t *ti; - gme_err = gme_track_info(emu, &ti, song_num); - if (gme_err != nullptr) { - LogWarning(gme_domain, gme_err); - gme_delete(emu); - return false; - } - - assert(ti != nullptr); - - if (ti->play_length > 0) + if (info.play_length > 0) tag_handler_invoke_duration(handler, handler_ctx, - SongTime::FromMS(ti->play_length)); + SongTime::FromMS(info.play_length)); - if (ti->song != nullptr) { - if (gme_track_count(emu) > 1) { + if (info.song != nullptr) { + if (track_count > 1) { /* start numbering subtunes from 1 */ char tag_title[1024]; snprintf(tag_title, sizeof(tag_title), - "%s (%d/%d)", - ti->song, song_num + 1, - gme_track_count(emu)); + "%s (%u/%d)", + info.song, song_num + 1, + track_count); tag_handler_invoke_tag(handler, handler_ctx, TAG_TITLE, tag_title); } else tag_handler_invoke_tag(handler, handler_ctx, - TAG_TITLE, ti->song); + TAG_TITLE, info.song); } - if (ti->author != nullptr) + if (info.author != nullptr) tag_handler_invoke_tag(handler, handler_ctx, - TAG_ARTIST, ti->author); + TAG_ARTIST, info.author); - if (ti->game != nullptr) + if (info.game != nullptr) tag_handler_invoke_tag(handler, handler_ctx, - TAG_ALBUM, ti->game); + TAG_ALBUM, info.game); - if (ti->comment != nullptr) + if (info.comment != nullptr) tag_handler_invoke_tag(handler, handler_ctx, - TAG_COMMENT, ti->comment); + TAG_COMMENT, info.comment); - if (ti->copyright != nullptr) + if (info.copyright != nullptr) tag_handler_invoke_tag(handler, handler_ctx, - TAG_DATE, ti->copyright); + TAG_DATE, info.copyright); +} + +static bool +ScanMusicEmu(Music_Emu *emu, unsigned song_num, + const struct tag_handler *handler, void *handler_ctx) +{ + gme_info_t *ti; + const char *gme_err = gme_track_info(emu, &ti, song_num); + if (gme_err != nullptr) { + LogWarning(gme_domain, gme_err); + return false; + } + + assert(ti != nullptr); + + ScanGmeInfo(*ti, song_num, gme_track_count(emu), + handler, handler_ctx); gme_free_info(ti); + return true; +} + +static bool +gme_scan_file(Path path_fs, + const struct tag_handler *handler, void *handler_ctx) +{ + const auto container = ParseContainerPath(path_fs); + + Music_Emu *emu; + const char *gme_err = + gme_open_file(container.path.c_str(), &emu, GME_SAMPLE_RATE); + if (gme_err != nullptr) { + LogWarning(gme_domain, gme_err); + return false; + } + + const bool result = ScanMusicEmu(emu, container.track, handler, handler_ctx); + gme_delete(emu); - return true; + return result; } static const char *const gme_suffixes[] = { @@ -289,7 +303,7 @@ static const char *const gme_suffixes[] = { extern const struct DecoderPlugin gme_decoder_plugin; const struct DecoderPlugin gme_decoder_plugin = { "gme", - nullptr, + gme_plugin_init, nullptr, nullptr, gme_file_decode, diff --git a/src/decoder/plugins/GmeDecoderPlugin.hxx b/src/decoder/plugins/GmeDecoderPlugin.hxx index f4885b6e4..07f78d9e2 100644 --- a/src/decoder/plugins/GmeDecoderPlugin.hxx +++ b/src/decoder/plugins/GmeDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index de6c9b127..9baa6fb9c 100644 --- a/src/decoder/plugins/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include <mad.h> -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG #include <id3tag.h> #endif @@ -107,9 +107,9 @@ mad_fixed_to_24_buffer(int32_t *dest, const struct mad_synth *synth, } static bool -mp3_plugin_init(gcc_unused const config_param ¶m) +mp3_plugin_init(gcc_unused const ConfigBlock &block) { - gapless_playback = config_get_bool(CONF_GAPLESS_MP3_PLAYBACK, + gapless_playback = config_get_bool(ConfigOption::GAPLESS_MP3_PLAYBACK, DEFAULT_GAPLESS_MP3_PLAYBACK); return true; } @@ -167,6 +167,15 @@ struct MadDecoder { bool DecodeFirstFrame(Tag **tag); + void AllocateBuffers() { + assert(max_frames > 0); + assert(frame_offsets == nullptr); + assert(times == nullptr); + + frame_offsets = new long[max_frames]; + times = new mad_timer_t[max_frames]; + } + gcc_pure long TimeToFrame(SongTime t) const; @@ -251,7 +260,7 @@ MadDecoder::FillBuffer() return true; } -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG static bool parse_id3_replay_gain_info(ReplayGainInfo &rgi, struct id3_tag *tag) @@ -285,7 +294,7 @@ parse_id3_replay_gain_info(ReplayGainInfo &rgi, } #endif -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG gcc_pure static MixRampInfo parse_id3_mixramp(struct id3_tag *tag) @@ -317,7 +326,7 @@ parse_id3_mixramp(struct id3_tag *tag) inline void MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) { -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG id3_byte_t *allocated = nullptr; const id3_length_t count = stream.bufend - stream.this_frame; @@ -369,7 +378,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) id3_tag_delete(id3_tag); delete[] allocated; -#else /* !HAVE_ID3TAG */ +#else /* !ENABLE_ID3TAG */ (void)mpd_tag; /* This code is enabled when libid3tag is disabled. Instead @@ -386,7 +395,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag) #endif } -#ifndef HAVE_ID3TAG +#ifndef ENABLE_ID3TAG /** * This function emulates libid3tag when it is disabled. Instead of * doing a real analyzation of the frame, it just checks whether the @@ -402,7 +411,7 @@ id3_tag_query(const void *p0, size_t length) ? (p[8] << 7) + p[9] + 10 : 0; } -#endif /* !HAVE_ID3TAG */ +#endif /* !ENABLE_ID3TAG */ static enum mp3_action RecoverFrameError(struct mad_stream &stream) @@ -504,10 +513,10 @@ struct xing { enum xing_magic magic; /* header magic */ }; -static const unsigned XING_FRAMES = 1; -static const unsigned XING_BYTES = 2; -static const unsigned XING_TOC = 4; -static const unsigned XING_SCALE = 8; +static constexpr unsigned XING_FRAMES = 1; +static constexpr unsigned XING_BYTES = 2; +static constexpr unsigned XING_TOC = 4; +static constexpr unsigned XING_SCALE = 8; struct lame_version { unsigned major; @@ -819,9 +828,6 @@ MadDecoder::DecodeFirstFrame(Tag **tag) return false; } - frame_offsets = new long[max_frames]; - times = new mad_timer_t[max_frames]; - return true; } @@ -1049,6 +1055,8 @@ mp3_decode(Decoder &decoder, InputStream &input_stream) return; } + data.AllocateBuffers(); + Error error; AudioFormat audio_format; if (!audio_format_init_checked(audio_format, diff --git a/src/decoder/plugins/MadDecoderPlugin.hxx b/src/decoder/plugins/MadDecoderPlugin.hxx index eb2a10d6f..c06f7341a 100644 --- a/src/decoder/plugins/MadDecoderPlugin.hxx +++ b/src/decoder/plugins/MadDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/MikmodDecoderPlugin.cxx b/src/decoder/plugins/MikmodDecoderPlugin.cxx index 85633f1fc..135bae15a 100644 --- a/src/decoder/plugins/MikmodDecoderPlugin.cxx +++ b/src/decoder/plugins/MikmodDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -109,15 +109,15 @@ static bool mikmod_loop; static unsigned mikmod_sample_rate; static bool -mikmod_decoder_init(const config_param ¶m) +mikmod_decoder_init(const ConfigBlock &block) { static char params[] = ""; - mikmod_loop = param.GetBlockValue("loop", false); - mikmod_sample_rate = param.GetBlockValue("sample_rate", 44100u); + mikmod_loop = block.GetBlockValue("loop", false); + mikmod_sample_rate = block.GetBlockValue("sample_rate", 44100u); if (!audio_valid_sample_rate(mikmod_sample_rate)) FormatFatalError("Invalid sample rate in line %d: %u", - param.line, mikmod_sample_rate); + block.line, mikmod_sample_rate); md_device = 0; md_reverb = 0; diff --git a/src/decoder/plugins/MikmodDecoderPlugin.hxx b/src/decoder/plugins/MikmodDecoderPlugin.hxx index 27ba2a823..508c7ae28 100644 --- a/src/decoder/plugins/MikmodDecoderPlugin.hxx +++ b/src/decoder/plugins/MikmodDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/ModplugDecoderPlugin.cxx b/src/decoder/plugins/ModplugDecoderPlugin.cxx index 3e0a41550..153392929 100644 --- a/src/decoder/plugins/ModplugDecoderPlugin.cxx +++ b/src/decoder/plugins/ModplugDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,12 +41,12 @@ static constexpr offset_type MODPLUG_FILE_LIMIT = 100 * 1024 * 1024; static int modplug_loop_count; static bool -modplug_decoder_init(const config_param ¶m) +modplug_decoder_init(const ConfigBlock &block) { - modplug_loop_count = param.GetBlockValue("loop_count", 0); + modplug_loop_count = block.GetBlockValue("loop_count", 0); if (modplug_loop_count < -1) FormatFatalError("Invalid loop count in line %d: %i", - param.line, modplug_loop_count); + block.line, modplug_loop_count); return true; } diff --git a/src/decoder/plugins/ModplugDecoderPlugin.hxx b/src/decoder/plugins/ModplugDecoderPlugin.hxx index 08f2ecb12..5d20afc7f 100644 --- a/src/decoder/plugins/ModplugDecoderPlugin.hxx +++ b/src/decoder/plugins/ModplugDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx index 391b5d691..e1ddd05f0 100644 --- a/src/decoder/plugins/MpcdecDecoderPlugin.cxx +++ b/src/decoder/plugins/MpcdecDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -134,8 +134,6 @@ mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest, static void mpcdec_decode(Decoder &mpd_decoder, InputStream &is) { - MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH]; - mpc_decoder_data data(is, &mpd_decoder); mpc_reader reader; @@ -195,8 +193,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is) decoder_seek_error(mpd_decoder); } - mpc_uint32_t vbr_update_bits = 0; - + MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH]; mpc_frame_info frame; frame.buffer = (MPC_SAMPLE_FORMAT *)sample_buffer; mpc_status status = mpc_demux_decode(demux, &frame); @@ -215,8 +212,8 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is) MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)]; mpc_to_mpd_buffer(chunk, sample_buffer, ret); - long bit_rate = vbr_update_bits * audio_format.sample_rate - / 1152 / 1000; + long bit_rate = unsigned(frame.bits) * audio_format.sample_rate + / (1000 * frame.samples); cmd = decoder_data(mpd_decoder, is, chunk, ret * sizeof(chunk[0]), diff --git a/src/decoder/plugins/MpcdecDecoderPlugin.hxx b/src/decoder/plugins/MpcdecDecoderPlugin.hxx index 7f71311fa..86216b4c2 100644 --- a/src/decoder/plugins/MpcdecDecoderPlugin.hxx +++ b/src/decoder/plugins/MpcdecDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/Mpg123DecoderPlugin.cxx b/src/decoder/plugins/Mpg123DecoderPlugin.cxx index 166529a4d..43c0c868f 100644 --- a/src/decoder/plugins/Mpg123DecoderPlugin.cxx +++ b/src/decoder/plugins/Mpg123DecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" +#include "util/StringView.hxx" #include "Log.hxx" #include <mpg123.h> @@ -37,7 +38,7 @@ static constexpr Domain mpg123_domain("mpg123"); static bool -mpd_mpg123_init(gcc_unused const config_param ¶m) +mpd_mpg123_init(gcc_unused const ConfigBlock &block) { mpg123_init(); @@ -111,7 +112,7 @@ AddTagItem(TagBuilder &tag, TagType type, const mpg123_string &s) assert(s.size >= s.fill); assert(s.fill > 0); - tag.AddItem(type, s.p, s.fill - 1); + tag.AddItem(type, {s.p, s.fill - 1}); } static void diff --git a/src/decoder/plugins/Mpg123DecoderPlugin.hxx b/src/decoder/plugins/Mpg123DecoderPlugin.hxx index fd089c6a4..e09e6f956 100644 --- a/src/decoder/plugins/Mpg123DecoderPlugin.hxx +++ b/src/decoder/plugins/Mpg123DecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggCodec.cxx b/src/decoder/plugins/OggCodec.cxx index c7f39586e..cd35609aa 100644 --- a/src/decoder/plugins/OggCodec.cxx +++ b/src/decoder/plugins/OggCodec.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggCodec.hxx b/src/decoder/plugins/OggCodec.hxx index 3b096561c..a47ae566e 100644 --- a/src/decoder/plugins/OggCodec.hxx +++ b/src/decoder/plugins/OggCodec.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggFind.cxx b/src/decoder/plugins/OggFind.cxx index 978e1d7cf..b452c0a00 100644 --- a/src/decoder/plugins/OggFind.cxx +++ b/src/decoder/plugins/OggFind.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggFind.hxx b/src/decoder/plugins/OggFind.hxx index 2aa4f6c06..47a80a232 100644 --- a/src/decoder/plugins/OggFind.hxx +++ b/src/decoder/plugins/OggFind.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggSyncState.hxx b/src/decoder/plugins/OggSyncState.hxx index 024902fff..1641b4aa1 100644 --- a/src/decoder/plugins/OggSyncState.hxx +++ b/src/decoder/plugins/OggSyncState.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggUtil.cxx b/src/decoder/plugins/OggUtil.cxx index 6341b0008..a7812238d 100644 --- a/src/decoder/plugins/OggUtil.cxx +++ b/src/decoder/plugins/OggUtil.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OggUtil.hxx b/src/decoder/plugins/OggUtil.hxx index 94c380ef4..b9bfe17de 100644 --- a/src/decoder/plugins/OggUtil.hxx +++ b/src/decoder/plugins/OggUtil.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index e14827e38..0d03566a7 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -61,7 +61,7 @@ IsOpusTags(const ogg_packet &packet) } static bool -mpd_opus_init(gcc_unused const config_param ¶m) +mpd_opus_init(gcc_unused const ConfigBlock &block) { LogDebug(opus_domain, opus_get_version_string()); diff --git a/src/decoder/plugins/OpusDecoderPlugin.h b/src/decoder/plugins/OpusDecoderPlugin.h index 260dab99a..c6a8e921d 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.h +++ b/src/decoder/plugins/OpusDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusDomain.cxx b/src/decoder/plugins/OpusDomain.cxx index 1efd64a48..d65c9c14a 100644 --- a/src/decoder/plugins/OpusDomain.cxx +++ b/src/decoder/plugins/OpusDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusDomain.hxx b/src/decoder/plugins/OpusDomain.hxx index fb19e0301..813bc097c 100644 --- a/src/decoder/plugins/OpusDomain.hxx +++ b/src/decoder/plugins/OpusDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusHead.cxx b/src/decoder/plugins/OpusHead.cxx index bfa41d618..3357fdddb 100644 --- a/src/decoder/plugins/OpusHead.cxx +++ b/src/decoder/plugins/OpusHead.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusHead.hxx b/src/decoder/plugins/OpusHead.hxx index c478d8d90..b04386ff8 100644 --- a/src/decoder/plugins/OpusHead.hxx +++ b/src/decoder/plugins/OpusHead.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusReader.hxx b/src/decoder/plugins/OpusReader.hxx index c5b8e9107..c449a88d1 100644 --- a/src/decoder/plugins/OpusReader.hxx +++ b/src/decoder/plugins/OpusReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusTags.cxx b/src/decoder/plugins/OpusTags.cxx index aff5479c0..a77a0f71a 100644 --- a/src/decoder/plugins/OpusTags.cxx +++ b/src/decoder/plugins/OpusTags.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/OpusTags.hxx b/src/decoder/plugins/OpusTags.hxx index be3ac3a8d..57c46882e 100644 --- a/src/decoder/plugins/OpusTags.hxx +++ b/src/decoder/plugins/OpusTags.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/PcmDecoderPlugin.cxx b/src/decoder/plugins/PcmDecoderPlugin.cxx index c07a7b9b1..d9a8110de 100644 --- a/src/decoder/plugins/PcmDecoderPlugin.cxx +++ b/src/decoder/plugins/PcmDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/PcmDecoderPlugin.hxx b/src/decoder/plugins/PcmDecoderPlugin.hxx index 3582e5856..0fd0e6b26 100644 --- a/src/decoder/plugins/PcmDecoderPlugin.hxx +++ b/src/decoder/plugins/PcmDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index 8435f095f..0d17e98c4 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,87 +22,64 @@ #include "../DecoderAPI.hxx" #include "tag/TagHandler.hxx" #include "fs/Path.hxx" +#include "fs/AllocatedPath.hxx" #include "util/FormatString.hxx" #include "util/Domain.hxx" +#include "util/Error.hxx" #include "system/ByteOrder.hxx" +#include "system/FatalError.hxx" #include "Log.hxx" -#include <errno.h> -#include <stdlib.h> #include <string.h> -#include <glib.h> #include <sidplay/sidplay2.h> #include <sidplay/builders/resid.h> #include <sidplay/utils/SidTuneMod.h> +#include <sidplay/utils/SidDatabase.h> #define SUBTUNE_PREFIX "tune_" static constexpr Domain sidplay_domain("sidplay"); -static GPatternSpec *path_with_subtune; -static const char *songlength_file; -static GKeyFile *songlength_database; +static SidDatabase *songlength_database; static bool all_files_are_containers; static unsigned default_songlength; static bool filter_setting; -static GKeyFile * -sidplay_load_songlength_db(const char *path) +static SidDatabase * +sidplay_load_songlength_db(const Path path) { - GError *error = nullptr; - gchar *data; - gsize size; - - if (!g_file_get_contents(path, &data, &size, &error)) { + SidDatabase *db = new SidDatabase(); + if (db->open(path.c_str()) < 0) { FormatError(sidplay_domain, "unable to read songlengths file %s: %s", - path, error->message); - g_error_free(error); - return nullptr; - } - - /* replace any ; comment characters with # */ - for (gsize i = 0; i < size; i++) - if (data[i] == ';') - data[i] = '#'; - - GKeyFile *db = g_key_file_new(); - bool success = g_key_file_load_from_data(db, data, size, - G_KEY_FILE_NONE, &error); - g_free(data); - if (!success) { - FormatError(sidplay_domain, - "unable to parse songlengths file %s: %s", - path, error->message); - g_error_free(error); - g_key_file_free(db); + path.c_str(), db->error()); + delete db; return nullptr; } - g_key_file_set_list_separator(db, ' '); return db; } static bool -sidplay_init(const config_param ¶m) +sidplay_init(const ConfigBlock &block) { /* read the songlengths database file */ - songlength_file = param.GetBlockValue("songlength_database"); - if (songlength_file != nullptr) - songlength_database = sidplay_load_songlength_db(songlength_file); + Error error; + const auto database_path = block.GetBlockPath("songlength_database", error); + if (!database_path.IsNull()) + songlength_database = sidplay_load_songlength_db(database_path); + else if (error.IsDefined()) + FatalError(error); - default_songlength = param.GetBlockValue("default_songlength", 0u); + default_songlength = block.GetBlockValue("default_songlength", 0u); all_files_are_containers = - param.GetBlockValue("all_files_are_containers", true); + block.GetBlockValue("all_files_are_containers", true); - path_with_subtune=g_pattern_spec_new( - "*/" SUBTUNE_PREFIX "???.sid"); - - filter_setting = param.GetBlockValue("filter", true); + filter_setting = block.GetBlockValue("filter", true); return true; } @@ -110,96 +87,61 @@ sidplay_init(const config_param ¶m) static void sidplay_finish() { - g_pattern_spec_free(path_with_subtune); - - if(songlength_database) - g_key_file_free(songlength_database); + delete songlength_database; } -/** - * returns the file path stripped of any /tune_xxx.sid subtune - * suffix - */ -static char * -get_container_name(Path path_fs) +struct SidplayContainerPath { + AllocatedPath path; + unsigned track; +}; + +gcc_pure +static unsigned +ParseSubtuneName(const char *base) { - char *path_container = strdup(path_fs.c_str()); + if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0) + return 0; - if(!g_pattern_match(path_with_subtune, - strlen(path_container), path_container, nullptr)) - return path_container; + base += sizeof(SUBTUNE_PREFIX) - 1; - char *ptr=g_strrstr(path_container, "/" SUBTUNE_PREFIX); - if(ptr) *ptr='\0'; + char *endptr; + auto track = strtoul(base, &endptr, 10); + if (endptr == base || *endptr != '.') + return 0; - return path_container; + return track; } /** - * returns tune number from file.sid/tune_xxx.sid style path or 1 if - * no subtune is appended + * returns the file path stripped of any /tune_xxx.* subtune suffix + * and the track number (or 1 if no "tune_xxx" suffix is present). */ -static unsigned -get_song_num(const char *path_fs) +static SidplayContainerPath +ParseContainerPath(Path path_fs) { - if(g_pattern_match(path_with_subtune, - strlen(path_fs), path_fs, nullptr)) { - char *sub=g_strrstr(path_fs, "/" SUBTUNE_PREFIX); - if(!sub) return 1; - - sub+=strlen("/" SUBTUNE_PREFIX); - int song_num=strtol(sub, nullptr, 10); - - if (errno == EINVAL) - return 1; - else - return song_num; - } else - return 1; + const Path base = path_fs.GetBase(); + unsigned track; + if (base.IsNull() || + (track = ParseSubtuneName(base.c_str())) < 1) + return { AllocatedPath(path_fs), 1 }; + + return { path_fs.GetDirectoryName(), track }; } /* get the song length in seconds */ static SignedSongTime -get_song_length(Path path_fs) +get_song_length(SidTuneMod &tune) { + assert(tune); + if (songlength_database == nullptr) return SignedSongTime::Negative(); - char *sid_file = get_container_name(path_fs); - SidTuneMod tune(sid_file); - free(sid_file); - if(!tune) { - LogWarning(sidplay_domain, - "failed to load file for calculating md5 sum"); + const auto length = songlength_database->length(tune); + if (length < 0) return SignedSongTime::Negative(); - } - char md5sum[SIDTUNE_MD5_LENGTH+1]; - tune.createMD5(md5sum); - - const unsigned song_num = get_song_num(path_fs.c_str()); - gsize num_items; - gchar **values=g_key_file_get_string_list(songlength_database, - "Database", md5sum, &num_items, nullptr); - if(!values || song_num>num_items) { - g_strfreev(values); - return SignedSongTime::Negative(); - } - - int minutes=strtol(values[song_num-1], nullptr, 10); - if(errno==EINVAL) minutes=0; - - int seconds; - char *ptr=strchr(values[song_num-1], ':'); - if(ptr) { - seconds=strtol(ptr+1, nullptr, 10); - if(errno==EINVAL) seconds=0; - } else - seconds=0; - - g_strfreev(values); - - return SignedSongTime::FromS((minutes * 60) + seconds); + return SignedSongTime::FromS(length); } static void @@ -209,18 +151,17 @@ sidplay_file_decode(Decoder &decoder, Path path_fs) /* load the tune */ - char *path_container=get_container_name(path_fs); - SidTune tune(path_container, nullptr, true); - free(path_container); + const auto container = ParseContainerPath(path_fs); + SidTuneMod tune(container.path.c_str()); if (!tune) { LogWarning(sidplay_domain, "failed to load file"); return; } - const int song_num = get_song_num(path_fs.c_str()); + const int song_num = container.track; tune.selectSong(song_num); - auto duration = get_song_length(path_fs); + auto duration = get_song_length(tune); if (duration.IsNegative() && default_songlength > 0) duration = SongTime::FromS(default_songlength); @@ -347,14 +288,15 @@ static bool sidplay_scan_file(Path path_fs, const struct tag_handler *handler, void *handler_ctx) { - const int song_num = get_song_num(path_fs.c_str()); - char *path_container=get_container_name(path_fs); + const auto container = ParseContainerPath(path_fs); + const unsigned song_num = container.track; - SidTune tune(path_container, nullptr, true); - free(path_container); + SidTuneMod tune(container.path.c_str()); if (!tune) return false; + tune.selectSong(song_num); + const SidTuneInfo &info = tune.getInfo(); /* title */ @@ -385,7 +327,7 @@ sidplay_scan_file(Path path_fs, tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); /* time */ - const auto duration = get_song_length(path_fs); + const auto duration = get_song_length(tune); if (!duration.IsNegative()) tag_handler_invoke_duration(handler, handler_ctx, SongTime(duration)); diff --git a/src/decoder/plugins/SidplayDecoderPlugin.hxx b/src/decoder/plugins/SidplayDecoderPlugin.hxx index 58786e646..d54ab9fa6 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.hxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/SndfileDecoderPlugin.cxx b/src/decoder/plugins/SndfileDecoderPlugin.cxx index 5518c70be..d8e4b0ba4 100644 --- a/src/decoder/plugins/SndfileDecoderPlugin.cxx +++ b/src/decoder/plugins/SndfileDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -32,7 +32,7 @@ static constexpr Domain sndfile_domain("sndfile"); static bool -sndfile_init(gcc_unused const config_param ¶m) +sndfile_init(gcc_unused const ConfigBlock &block) { LogDebug(sndfile_domain, sf_version_string()); return true; @@ -125,7 +125,7 @@ sndfile_vio_tell(void *user_data) } /** - * This SF_VIRTUAL_IO implementation wraps MPD's #input_stream to a + * This SF_VIRTUAL_IO implementation wraps MPD's #InputStream to a * libsndfile stream. */ static SF_VIRTUAL_IO vio = { diff --git a/src/decoder/plugins/SndfileDecoderPlugin.hxx b/src/decoder/plugins/SndfileDecoderPlugin.hxx index d56acdd5a..c793e1356 100644 --- a/src/decoder/plugins/SndfileDecoderPlugin.hxx +++ b/src/decoder/plugins/SndfileDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/VorbisComments.cxx b/src/decoder/plugins/VorbisComments.cxx index 062f46acf..e742e32ff 100644 --- a/src/decoder/plugins/VorbisComments.cxx +++ b/src/decoder/plugins/VorbisComments.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #include "tag/ReplayGain.hxx" #include "ReplayGainInfo.hxx" #include "util/ASCII.hxx" -#include "util/SplitString.hxx" +#include "util/DivideString.hxx" #include <stddef.h> #include <stdlib.h> @@ -74,7 +74,7 @@ vorbis_scan_comment(const char *comment, const struct tag_handler *handler, void *handler_ctx) { if (handler->pair != nullptr) { - const SplitString split(comment, '='); + const DivideString split(comment, '='); if (split.IsDefined() && !split.IsEmpty()) tag_handler_invoke_pair(handler, handler_ctx, split.GetFirst(), diff --git a/src/decoder/plugins/VorbisComments.hxx b/src/decoder/plugins/VorbisComments.hxx index 893c89277..54912aed7 100644 --- a/src/decoder/plugins/VorbisComments.hxx +++ b/src/decoder/plugins/VorbisComments.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/VorbisDecoderPlugin.cxx b/src/decoder/plugins/VorbisDecoderPlugin.cxx index e0d3d1374..d7069a2f4 100644 --- a/src/decoder/plugins/VorbisDecoderPlugin.cxx +++ b/src/decoder/plugins/VorbisDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "../DecoderAPI.hxx" #include "input/InputStream.hxx" #include "OggCodec.hxx" +#include "pcm/Interleave.hxx" #include "util/Error.hxx" #include "util/Macros.hxx" #include "CheckAudioFormat.hxx" @@ -181,20 +182,15 @@ static void vorbis_interleave(float *dest, const float *const*src, unsigned nframes, unsigned channels) { - for (const float *const*src_end = src + channels; - src != src_end; ++src, ++dest) { - float *gcc_restrict d = dest; - for (const float *gcc_restrict s = *src, *s_end = s + nframes; - s != s_end; ++s, d += channels) - *d = *s; - } + PcmInterleaveFloat(dest, ConstBuffer<const float *>(src, channels), + nframes); } #endif /* public */ static bool -vorbis_init(gcc_unused const config_param ¶m) +vorbis_init(gcc_unused const ConfigBlock &block) { #ifndef HAVE_TREMOR LogDebug(vorbis_domain, vorbis_version_string()); @@ -263,7 +259,7 @@ vorbis_stream_decode(Decoder &decoder, unsigned kbit_rate = 0; DecoderCommand cmd = decoder_get_command(decoder); - do { + while (cmd != DecoderCommand::STOP) { if (cmd == DecoderCommand::SEEK) { auto seek_where = decoder_seek_where_frame(decoder); if (0 == ov_pcm_seek_page(&vf, seek_where)) { @@ -332,7 +328,7 @@ vorbis_stream_decode(Decoder &decoder, cmd = decoder_data(decoder, input_stream, buffer, nbytes, kbit_rate); - } while (cmd != DecoderCommand::STOP); + } ov_clear(&vf); } diff --git a/src/decoder/plugins/VorbisDecoderPlugin.h b/src/decoder/plugins/VorbisDecoderPlugin.h index b54df2e97..1e2aee47e 100644 --- a/src/decoder/plugins/VorbisDecoderPlugin.h +++ b/src/decoder/plugins/VorbisDecoderPlugin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/VorbisDomain.cxx b/src/decoder/plugins/VorbisDomain.cxx index e3d880efa..2855e325d 100644 --- a/src/decoder/plugins/VorbisDomain.cxx +++ b/src/decoder/plugins/VorbisDomain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/VorbisDomain.hxx b/src/decoder/plugins/VorbisDomain.hxx index 48715e328..bd63e5948 100644 --- a/src/decoder/plugins/VorbisDomain.hxx +++ b/src/decoder/plugins/VorbisDomain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/WavpackDecoderPlugin.cxx b/src/decoder/plugins/WavpackDecoderPlugin.cxx index 67859bbd2..339fd6910 100644 --- a/src/decoder/plugins/WavpackDecoderPlugin.cxx +++ b/src/decoder/plugins/WavpackDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,10 +28,10 @@ #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/Macros.hxx" +#include "util/Alloc.hxx" #include "Log.hxx" #include <wavpack/wavpack.h> -#include <glib.h> #include <assert.h> #include <stdio.h> @@ -322,7 +322,7 @@ wavpack_scan_file(Path path_fs, } /* - * mpd input_stream <=> WavpackStreamReader wrapper callbacks + * #InputStream <=> WavpackStreamReader wrapper callbacks */ /* This struct is needed for per-stream last_byte storage. */ @@ -484,10 +484,10 @@ wavpack_open_wvc(Decoder &decoder, const char *uri) if (uri == nullptr) return nullptr; - char *wvc_url = g_strconcat(uri, "c", nullptr); + char *wvc_url = xstrcatdup(uri, "c"); InputStream *is_wvc = decoder_open_uri(decoder, uri, IgnoreError()); - g_free(wvc_url); + free(wvc_url); if (is_wvc == nullptr) return nullptr; diff --git a/src/decoder/plugins/WavpackDecoderPlugin.hxx b/src/decoder/plugins/WavpackDecoderPlugin.hxx index 2e5f9bd42..f2ee72e25 100644 --- a/src/decoder/plugins/WavpackDecoderPlugin.hxx +++ b/src/decoder/plugins/WavpackDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/WildmidiDecoderPlugin.cxx b/src/decoder/plugins/WildmidiDecoderPlugin.cxx index fc58f0977..64fa33b05 100644 --- a/src/decoder/plugins/WildmidiDecoderPlugin.cxx +++ b/src/decoder/plugins/WildmidiDecoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -38,11 +38,11 @@ static constexpr Domain wildmidi_domain("wildmidi"); static constexpr unsigned WILDMIDI_SAMPLE_RATE = 48000; static bool -wildmidi_init(const config_param ¶m) +wildmidi_init(const ConfigBlock &block) { Error error; const AllocatedPath path = - param.GetBlockPath("config_file", + block.GetBlockPath("config_file", "/etc/timidity/timidity.cfg", error); if (path.IsNull()) diff --git a/src/decoder/plugins/WildmidiDecoderPlugin.hxx b/src/decoder/plugins/WildmidiDecoderPlugin.hxx index fc87aab80..ec896662b 100644 --- a/src/decoder/plugins/WildmidiDecoderPlugin.hxx +++ b/src/decoder/plugins/WildmidiDecoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/decoder/plugins/XiphTags.cxx b/src/decoder/plugins/XiphTags.cxx index 11a0bcd42..24a2aa623 100644 --- a/src/decoder/plugins/XiphTags.cxx +++ b/src/decoder/plugins/XiphTags.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,6 @@ const struct tag_table xiph_tags[] = { { "tracknumber", TAG_TRACK }, { "discnumber", TAG_DISC }, - { "album artist", TAG_ALBUM_ARTIST }, { "description", TAG_COMMENT }, { nullptr, TAG_NUM_OF_ITEM_TYPES } }; diff --git a/src/decoder/plugins/XiphTags.hxx b/src/decoder/plugins/XiphTags.hxx index 48a27425f..d83e116c1 100644 --- a/src/decoder/plugins/XiphTags.hxx +++ b/src/decoder/plugins/XiphTags.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/EncoderAPI.hxx b/src/encoder/EncoderAPI.hxx index b147eac21..def543c58 100644 --- a/src/encoder/EncoderAPI.hxx +++ b/src/encoder/EncoderAPI.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,10 +27,11 @@ // IWYU pragma: begin_exports +#include "EncoderInterface.hxx" #include "EncoderPlugin.hxx" #include "AudioFormat.hxx" #include "tag/Tag.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" // IWYU pragma: end_exports diff --git a/src/encoder/EncoderInterface.hxx b/src/encoder/EncoderInterface.hxx new file mode 100644 index 000000000..c1ecd872f --- /dev/null +++ b/src/encoder/EncoderInterface.hxx @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2003-2015 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_ENCODER_INTERFACE_HXX +#define MPD_ENCODER_INTERFACE_HXX + +#include "EncoderPlugin.hxx" + +#include <assert.h> + +struct Encoder { + const EncoderPlugin &plugin; + +#ifndef NDEBUG + bool open, pre_tag, tag, end; +#endif + + explicit Encoder(const EncoderPlugin &_plugin) + :plugin(_plugin) +#ifndef NDEBUG + , open(false) +#endif + {} + + + /** + * Frees an #Encoder object. + */ + void Dispose() { + assert(!open); + + plugin.finish(this); + } + + /** + * Opens the object. You must call this prior to using it. + * Before you free it, you must call Close(). You may open + * and close (reuse) one encoder any number of times. + * + * After this function returns successfully and before the + * first encoder_write() call, you should invoke + * encoder_read() to obtain the file header. + * + * @param audio_format the encoder's input audio format; the plugin + * may modify the struct to adapt it to its abilities + * @return true on success + */ + bool Open(AudioFormat &audio_format, Error &error) { + assert(!open); + + bool success = plugin.open(this, audio_format, error); +#ifndef NDEBUG + open = success; + pre_tag = tag = end = false; +#endif + return success; + } + + + /** + * Closes the object. This disables the encoder, and readies + * it for reusal by calling Open() again. + */ + void Close() { + assert(open); + + if (plugin.close != nullptr) + plugin.close(this); + +#ifndef NDEBUG + open = false; +#endif + } +}; + +/** + * Ends the stream: flushes the encoder object, generate an + * end-of-stream marker (if applicable), make everything which might + * currently be buffered available by encoder_read(). + * + * After this function has been called, the encoder may not be usable + * for more data, and only encoder_read() and Encoder::Close() can be + * called. + * + * @param encoder the encoder + * @return true on success + */ +static inline bool +encoder_end(Encoder *encoder, Error &error) +{ + assert(encoder->open); + assert(!encoder->end); + +#ifndef NDEBUG + encoder->end = true; +#endif + + /* this method is optional */ + return encoder->plugin.end != nullptr + ? encoder->plugin.end(encoder, error) + : true; +} + +/** + * Flushes an encoder object, make everything which might currently be + * buffered available by encoder_read(). + * + * @param encoder the encoder + * @return true on success + */ +static inline bool +encoder_flush(Encoder *encoder, Error &error) +{ + assert(encoder->open); + assert(!encoder->pre_tag); + assert(!encoder->tag); + assert(!encoder->end); + + /* this method is optional */ + return encoder->plugin.flush != nullptr + ? encoder->plugin.flush(encoder, error) + : true; +} + +/** + * Prepare for sending a tag to the encoder. This is used by some + * encoders to flush the previous sub-stream, in preparation to begin + * a new one. + * + * @param encoder the encoder + * @return true on success + */ +static inline bool +encoder_pre_tag(Encoder *encoder, Error &error) +{ + assert(encoder->open); + assert(!encoder->pre_tag); + assert(!encoder->tag); + assert(!encoder->end); + + /* this method is optional */ + bool success = encoder->plugin.pre_tag != nullptr + ? encoder->plugin.pre_tag(encoder, error) + : true; + +#ifndef NDEBUG + encoder->pre_tag = success; +#endif + return success; +} + +/** + * Sends a tag to the encoder. + * + * Instructions: call encoder_pre_tag(); then obtain flushed data with + * encoder_read(); finally call encoder_tag(). + * + * @param encoder the encoder + * @param tag the tag object + * @return true on success + */ +static inline bool +encoder_tag(Encoder *encoder, const Tag &tag, Error &error) +{ + assert(encoder->open); + assert(!encoder->pre_tag); + assert(encoder->tag); + assert(!encoder->end); + +#ifndef NDEBUG + encoder->tag = false; +#endif + + /* this method is optional */ + return encoder->plugin.tag != nullptr + ? encoder->plugin.tag(encoder, tag, error) + : true; +} + +/** + * Writes raw PCM data to the encoder. + * + * @param encoder the encoder + * @param data the buffer containing PCM samples + * @param length the length of the buffer in bytes + * @return true on success + */ +static inline bool +encoder_write(Encoder *encoder, const void *data, size_t length, + Error &error) +{ + assert(encoder->open); + assert(!encoder->pre_tag); + assert(!encoder->tag); + assert(!encoder->end); + + return encoder->plugin.write(encoder, data, length, error); +} + +/** + * Reads encoded data from the encoder. + * + * Call this repeatedly until no more data is returned. + * + * @param encoder the encoder + * @param dest the destination buffer to copy to + * @param length the maximum length of the destination buffer + * @return the number of bytes written to #dest + */ +static inline size_t +encoder_read(Encoder *encoder, void *dest, size_t length) +{ + assert(encoder->open); + assert(!encoder->pre_tag || !encoder->tag); + +#ifndef NDEBUG + if (encoder->pre_tag) { + encoder->pre_tag = false; + encoder->tag = true; + } +#endif + + return encoder->plugin.read(encoder, dest, length); +} + +/** + * Get mime type of encoded content. + * + * @return an constant string, nullptr on failure + */ +static inline const char * +encoder_get_mime_type(Encoder *encoder) +{ + /* this method is optional */ + return encoder->plugin.get_mime_type != nullptr + ? encoder->plugin.get_mime_type(encoder) + : nullptr; +} + +#endif diff --git a/src/encoder/EncoderList.cxx b/src/encoder/EncoderList.cxx index 4bca5a4fe..f074f610b 100644 --- a/src/encoder/EncoderList.cxx +++ b/src/encoder/EncoderList.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,16 +33,16 @@ const EncoderPlugin *const encoder_plugins[] = { &null_encoder_plugin, -#ifdef ENABLE_VORBIS_ENCODER +#ifdef ENABLE_VORBISENC &vorbis_encoder_plugin, #endif -#ifdef HAVE_OPUS +#ifdef ENABLE_OPUS &opus_encoder_plugin, #endif -#ifdef ENABLE_LAME_ENCODER +#ifdef ENABLE_LAME &lame_encoder_plugin, #endif -#ifdef ENABLE_TWOLAME_ENCODER +#ifdef ENABLE_TWOLAME &twolame_encoder_plugin, #endif #ifdef ENABLE_WAVE_ENCODER @@ -51,7 +51,7 @@ const EncoderPlugin *const encoder_plugins[] = { #ifdef ENABLE_FLAC_ENCODER &flac_encoder_plugin, #endif -#ifdef ENABLE_SHINE_ENCODER +#ifdef ENABLE_SHINE &shine_encoder_plugin, #endif nullptr diff --git a/src/encoder/EncoderList.hxx b/src/encoder/EncoderList.hxx index e18d8ec74..19a4dd6d8 100644 --- a/src/encoder/EncoderList.hxx +++ b/src/encoder/EncoderList.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/EncoderPlugin.hxx b/src/encoder/EncoderPlugin.hxx index 95e4e5838..09b68a0a8 100644 --- a/src/encoder/EncoderPlugin.hxx +++ b/src/encoder/EncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,35 +20,18 @@ #ifndef MPD_ENCODER_PLUGIN_HXX #define MPD_ENCODER_PLUGIN_HXX -#include <assert.h> -#include <stdbool.h> #include <stddef.h> -struct EncoderPlugin; +struct Encoder; struct AudioFormat; -struct config_param; +struct ConfigBlock; struct Tag; class Error; -struct Encoder { - const EncoderPlugin &plugin; - -#ifndef NDEBUG - bool open, pre_tag, tag, end; -#endif - - explicit Encoder(const EncoderPlugin &_plugin) - :plugin(_plugin) -#ifndef NDEBUG - , open(false) -#endif - {} -}; - struct EncoderPlugin { const char *name; - Encoder *(*init)(const config_param ¶m, + Encoder *(*init)(const ConfigBlock &block, Error &error); void (*finish)(Encoder *encoder); @@ -65,7 +48,7 @@ struct EncoderPlugin { bool (*pre_tag)(Encoder *encoder, Error &error); - bool (*tag)(Encoder *encoder, const Tag *tag, + bool (*tag)(Encoder *encoder, const Tag &tag, Error &error); bool (*write)(Encoder *encoder, @@ -81,241 +64,14 @@ struct EncoderPlugin { * Creates a new encoder object. * * @param plugin the encoder plugin - * @param param optional configuration * @param error location to store the error occurring, or nullptr to ignore errors. * @return an encoder object on success, nullptr on failure */ static inline Encoder * -encoder_init(const EncoderPlugin &plugin, const config_param ¶m, - Error &error_r) -{ - return plugin.init(param, error_r); -} - -/** - * Frees an encoder object. - * - * @param encoder the encoder - */ -static inline void -encoder_finish(Encoder *encoder) -{ - assert(!encoder->open); - - encoder->plugin.finish(encoder); -} - -/** - * Opens an encoder object. You must call this prior to using it. - * Before you free it, you must call encoder_close(). You may open - * and close (reuse) one encoder any number of times. - * - * After this function returns successfully and before the first - * encoder_write() call, you should invoke encoder_read() to obtain - * the file header. - * - * @param encoder the encoder - * @param audio_format the encoder's input audio format; the plugin - * may modify the struct to adapt it to its abilities - * @return true on success - */ -static inline bool -encoder_open(Encoder *encoder, AudioFormat &audio_format, +encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block, Error &error) { - assert(!encoder->open); - - bool success = encoder->plugin.open(encoder, audio_format, error); -#ifndef NDEBUG - encoder->open = success; - encoder->pre_tag = encoder->tag = encoder->end = false; -#endif - return success; -} - -/** - * Closes an encoder object. This disables the encoder, and readies - * it for reusal by calling encoder_open() again. - * - * @param encoder the encoder - */ -static inline void -encoder_close(Encoder *encoder) -{ - assert(encoder->open); - - if (encoder->plugin.close != nullptr) - encoder->plugin.close(encoder); - -#ifndef NDEBUG - encoder->open = false; -#endif -} - -/** - * Ends the stream: flushes the encoder object, generate an - * end-of-stream marker (if applicable), make everything which might - * currently be buffered available by encoder_read(). - * - * After this function has been called, the encoder may not be usable - * for more data, and only encoder_read() and encoder_close() can be - * called. - * - * @param encoder the encoder - * @return true on success - */ -static inline bool -encoder_end(Encoder *encoder, Error &error) -{ - assert(encoder->open); - assert(!encoder->end); - -#ifndef NDEBUG - encoder->end = true; -#endif - - /* this method is optional */ - return encoder->plugin.end != nullptr - ? encoder->plugin.end(encoder, error) - : true; -} - -/** - * Flushes an encoder object, make everything which might currently be - * buffered available by encoder_read(). - * - * @param encoder the encoder - * @return true on success - */ -static inline bool -encoder_flush(Encoder *encoder, Error &error) -{ - assert(encoder->open); - assert(!encoder->pre_tag); - assert(!encoder->tag); - assert(!encoder->end); - - /* this method is optional */ - return encoder->plugin.flush != nullptr - ? encoder->plugin.flush(encoder, error) - : true; -} - -/** - * Prepare for sending a tag to the encoder. This is used by some - * encoders to flush the previous sub-stream, in preparation to begin - * a new one. - * - * @param encoder the encoder - * @param tag the tag object - * @return true on success - */ -static inline bool -encoder_pre_tag(Encoder *encoder, Error &error) -{ - assert(encoder->open); - assert(!encoder->pre_tag); - assert(!encoder->tag); - assert(!encoder->end); - - /* this method is optional */ - bool success = encoder->plugin.pre_tag != nullptr - ? encoder->plugin.pre_tag(encoder, error) - : true; - -#ifndef NDEBUG - encoder->pre_tag = success; -#endif - return success; -} - -/** - * Sends a tag to the encoder. - * - * Instructions: call encoder_pre_tag(); then obtain flushed data with - * encoder_read(); finally call encoder_tag(). - * - * @param encoder the encoder - * @param tag the tag object - * @return true on success - */ -static inline bool -encoder_tag(Encoder *encoder, const Tag *tag, Error &error) -{ - assert(encoder->open); - assert(!encoder->pre_tag); - assert(encoder->tag); - assert(!encoder->end); - -#ifndef NDEBUG - encoder->tag = false; -#endif - - /* this method is optional */ - return encoder->plugin.tag != nullptr - ? encoder->plugin.tag(encoder, tag, error) - : true; -} - -/** - * Writes raw PCM data to the encoder. - * - * @param encoder the encoder - * @param data the buffer containing PCM samples - * @param length the length of the buffer in bytes - * @return true on success - */ -static inline bool -encoder_write(Encoder *encoder, const void *data, size_t length, - Error &error) -{ - assert(encoder->open); - assert(!encoder->pre_tag); - assert(!encoder->tag); - assert(!encoder->end); - - return encoder->plugin.write(encoder, data, length, error); -} - -/** - * Reads encoded data from the encoder. - * - * Call this repeatedly until no more data is returned. - * - * @param encoder the encoder - * @param dest the destination buffer to copy to - * @param length the maximum length of the destination buffer - * @return the number of bytes written to #dest - */ -static inline size_t -encoder_read(Encoder *encoder, void *dest, size_t length) -{ - assert(encoder->open); - assert(!encoder->pre_tag || !encoder->tag); - -#ifndef NDEBUG - if (encoder->pre_tag) { - encoder->pre_tag = false; - encoder->tag = true; - } -#endif - - return encoder->plugin.read(encoder, dest, length); -} - -/** - * Get mime type of encoded content. - * - * @param plugin the encoder plugin - * @return an constant string, nullptr on failure - */ -static inline const char * -encoder_get_mime_type(Encoder *encoder) -{ - /* this method is optional */ - return encoder->plugin.get_mime_type != nullptr - ? encoder->plugin.get_mime_type(encoder) - : nullptr; + return plugin.init(block, error); } #endif diff --git a/src/encoder/ToOutputStream.cxx b/src/encoder/ToOutputStream.cxx new file mode 100644 index 000000000..43345cf70 --- /dev/null +++ b/src/encoder/ToOutputStream.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003-2015 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 "ToOutputStream.hxx" +#include "EncoderInterface.hxx" +#include "fs/io/OutputStream.hxx" + +bool +EncoderToOutputStream(OutputStream &os, Encoder &encoder, Error &error) +{ + while (true) { + /* read from the encoder */ + + char buffer[32768]; + size_t nbytes = encoder_read(&encoder, buffer, sizeof(buffer)); + if (nbytes == 0) + return true; + + /* write everything to the stream */ + + if (!os.Write(buffer, nbytes, error)) + return false; + } +} diff --git a/src/encoder/ToOutputStream.hxx b/src/encoder/ToOutputStream.hxx new file mode 100644 index 000000000..e3fb7b908 --- /dev/null +++ b/src/encoder/ToOutputStream.hxx @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2003-2015 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_ENCODER_TO_OUTPUT_STREAM_HXX +#define MPD_ENCODER_TO_OUTPUT_STREAM_HXX + +#include "check.h" + +struct Encoder; +class OutputStream; +class Error; + +bool +EncoderToOutputStream(OutputStream &os, Encoder &encoder, Error &error); + +#endif diff --git a/src/encoder/plugins/FlacEncoderPlugin.cxx b/src/encoder/plugins/FlacEncoderPlugin.cxx index 9317b02ea..86a3588df 100644 --- a/src/encoder/plugins/FlacEncoderPlugin.cxx +++ b/src/encoder/plugins/FlacEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -56,21 +56,21 @@ struct flac_encoder { static constexpr Domain flac_encoder_domain("vorbis_encoder"); static bool -flac_encoder_configure(struct flac_encoder *encoder, const config_param ¶m, +flac_encoder_configure(struct flac_encoder *encoder, const ConfigBlock &block, gcc_unused Error &error) { - encoder->compression = param.GetBlockValue("compression", 5u); + encoder->compression = block.GetBlockValue("compression", 5u); return true; } static Encoder * -flac_encoder_init(const config_param ¶m, Error &error) +flac_encoder_init(const ConfigBlock &block, Error &error) { flac_encoder *encoder = new flac_encoder(); - /* load configuration from "param" */ - if (!flac_encoder_configure(encoder, param, error)) { + /* load configuration from "block" */ + if (!flac_encoder_configure(encoder, block, error)) { /* configuration has failed, roll back and return error */ delete encoder; return nullptr; diff --git a/src/encoder/plugins/FlacEncoderPlugin.hxx b/src/encoder/plugins/FlacEncoderPlugin.hxx index 0cdc01600..dcd899974 100644 --- a/src/encoder/plugins/FlacEncoderPlugin.hxx +++ b/src/encoder/plugins/FlacEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/LameEncoderPlugin.cxx b/src/encoder/plugins/LameEncoderPlugin.cxx index 3878b52bb..7c6313109 100644 --- a/src/encoder/plugins/LameEncoderPlugin.cxx +++ b/src/encoder/plugins/LameEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -47,18 +47,18 @@ struct LameEncoder final { LameEncoder():encoder(lame_encoder_plugin) {} - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); }; static constexpr Domain lame_encoder_domain("lame_encoder"); bool -LameEncoder::Configure(const config_param ¶m, Error &error) +LameEncoder::Configure(const ConfigBlock &block, Error &error) { const char *value; char *endptr; - value = param.GetBlockValue("quality"); + value = block.GetBlockValue("quality"); if (value != nullptr) { /* a quality was configured (VBR) */ @@ -72,7 +72,7 @@ LameEncoder::Configure(const config_param ¶m, Error &error) return false; } - if (param.GetBlockValue("bitrate") != nullptr) { + if (block.GetBlockValue("bitrate") != nullptr) { error.Set(config_domain, "quality and bitrate are both defined"); return false; @@ -80,7 +80,7 @@ LameEncoder::Configure(const config_param ¶m, Error &error) } else { /* a bit rate was configured */ - value = param.GetBlockValue("bitrate"); + value = block.GetBlockValue("bitrate"); if (value == nullptr) { error.Set(config_domain, "neither bitrate nor quality defined"); @@ -101,12 +101,12 @@ LameEncoder::Configure(const config_param ¶m, Error &error) } static Encoder * -lame_encoder_init(const config_param ¶m, Error &error) +lame_encoder_init(const ConfigBlock &block, Error &error) { LameEncoder *encoder = new LameEncoder(); - /* load configuration from "param" */ - if (!encoder->Configure(param, error)) { + /* load configuration from "block" */ + if (!encoder->Configure(block, error)) { /* configuration has failed, roll back and return error */ delete encoder; return nullptr; diff --git a/src/encoder/plugins/LameEncoderPlugin.hxx b/src/encoder/plugins/LameEncoderPlugin.hxx index 03e398f67..5a077f7cc 100644 --- a/src/encoder/plugins/LameEncoderPlugin.hxx +++ b/src/encoder/plugins/LameEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/NullEncoderPlugin.cxx b/src/encoder/plugins/NullEncoderPlugin.cxx index 1d571d465..99be7aec4 100644 --- a/src/encoder/plugins/NullEncoderPlugin.cxx +++ b/src/encoder/plugins/NullEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ struct NullEncoder final { }; static Encoder * -null_encoder_init(gcc_unused const config_param ¶m, +null_encoder_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { NullEncoder *encoder = new NullEncoder(); diff --git a/src/encoder/plugins/NullEncoderPlugin.hxx b/src/encoder/plugins/NullEncoderPlugin.hxx index 6acf88e49..9fabe81fd 100644 --- a/src/encoder/plugins/NullEncoderPlugin.hxx +++ b/src/encoder/plugins/NullEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/OggSerial.cxx b/src/encoder/plugins/OggSerial.cxx index 677829439..639d2b3c1 100644 --- a/src/encoder/plugins/OggSerial.cxx +++ b/src/encoder/plugins/OggSerial.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/OggSerial.hxx b/src/encoder/plugins/OggSerial.hxx index ceba8ebf9..21ae02804 100644 --- a/src/encoder/plugins/OggSerial.hxx +++ b/src/encoder/plugins/OggSerial.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/OggStream.hxx b/src/encoder/plugins/OggStream.hxx index 805238c1d..376a697a1 100644 --- a/src/encoder/plugins/OggStream.hxx +++ b/src/encoder/plugins/OggStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/OpusEncoderPlugin.cxx b/src/encoder/plugins/OpusEncoderPlugin.cxx index 2b52228cb..e17cb86fd 100644 --- a/src/encoder/plugins/OpusEncoderPlugin.cxx +++ b/src/encoder/plugins/OpusEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -73,9 +73,9 @@ static constexpr Domain opus_encoder_domain("opus_encoder"); static bool opus_encoder_configure(struct opus_encoder *encoder, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { - const char *value = param.GetBlockValue("bitrate", "auto"); + const char *value = block.GetBlockValue("bitrate", "auto"); if (strcmp(value, "auto") == 0) encoder->bitrate = OPUS_AUTO; else if (strcmp(value, "max") == 0) @@ -90,13 +90,13 @@ opus_encoder_configure(struct opus_encoder *encoder, } } - encoder->complexity = param.GetBlockValue("complexity", 10u); + encoder->complexity = block.GetBlockValue("complexity", 10u); if (encoder->complexity > 10) { error.Format(config_domain, "Invalid complexity"); return false; } - value = param.GetBlockValue("signal", "auto"); + value = block.GetBlockValue("signal", "auto"); if (strcmp(value, "auto") == 0) encoder->signal = OPUS_AUTO; else if (strcmp(value, "voice") == 0) @@ -112,12 +112,12 @@ opus_encoder_configure(struct opus_encoder *encoder, } static Encoder * -opus_encoder_init(const config_param ¶m, Error &error) +opus_encoder_init(const ConfigBlock &block, Error &error) { opus_encoder *encoder = new opus_encoder(); - /* load configuration from "param" */ - if (!opus_encoder_configure(encoder, param, error)) { + /* load configuration from "block" */ + if (!opus_encoder_configure(encoder, block, error)) { /* configuration has failed, roll back and return error */ delete encoder; return nullptr; diff --git a/src/encoder/plugins/OpusEncoderPlugin.hxx b/src/encoder/plugins/OpusEncoderPlugin.hxx index 4e71694b9..09067e37d 100644 --- a/src/encoder/plugins/OpusEncoderPlugin.hxx +++ b/src/encoder/plugins/OpusEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/ShineEncoderPlugin.cxx b/src/encoder/plugins/ShineEncoderPlugin.cxx index 1b00f7d53..34040b018 100644 --- a/src/encoder/plugins/ShineEncoderPlugin.cxx +++ b/src/encoder/plugins/ShineEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -52,7 +52,7 @@ struct ShineEncoder { ShineEncoder():encoder(shine_encoder_plugin){} - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); bool Setup(Error &error); @@ -60,22 +60,21 @@ struct ShineEncoder { }; inline bool -ShineEncoder::Configure(const config_param ¶m, - gcc_unused Error &error) +ShineEncoder::Configure(const ConfigBlock &block, gcc_unused Error &error) { shine_set_config_mpeg_defaults(&config.mpeg); - config.mpeg.bitr = param.GetBlockValue("bitrate", 128); + config.mpeg.bitr = block.GetBlockValue("bitrate", 128); return true; } static Encoder * -shine_encoder_init(const config_param ¶m, Error &error) +shine_encoder_init(const ConfigBlock &block, Error &error) { ShineEncoder *encoder = new ShineEncoder(); - /* load configuration from "param" */ - if (!encoder->Configure(param, error)) { + /* load configuration from "block" */ + if (!encoder->Configure(block, error)) { /* configuration has failed, roll back and return error */ delete encoder; return nullptr; diff --git a/src/encoder/plugins/ShineEncoderPlugin.hxx b/src/encoder/plugins/ShineEncoderPlugin.hxx index 8b1520a74..ecde902fb 100644 --- a/src/encoder/plugins/ShineEncoderPlugin.hxx +++ b/src/encoder/plugins/ShineEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/TwolameEncoderPlugin.cxx b/src/encoder/plugins/TwolameEncoderPlugin.cxx index 2eb6b2b1c..262fc6c33 100644 --- a/src/encoder/plugins/TwolameEncoderPlugin.cxx +++ b/src/encoder/plugins/TwolameEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -53,18 +53,18 @@ struct TwolameEncoder final { TwolameEncoder():encoder(twolame_encoder_plugin) {} - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); }; static constexpr Domain twolame_encoder_domain("twolame_encoder"); bool -TwolameEncoder::Configure(const config_param ¶m, Error &error) +TwolameEncoder::Configure(const ConfigBlock &block, Error &error) { const char *value; char *endptr; - value = param.GetBlockValue("quality"); + value = block.GetBlockValue("quality"); if (value != nullptr) { /* a quality was configured (VBR) */ @@ -78,7 +78,7 @@ TwolameEncoder::Configure(const config_param ¶m, Error &error) return false; } - if (param.GetBlockValue("bitrate") != nullptr) { + if (block.GetBlockValue("bitrate") != nullptr) { error.Set(config_domain, "quality and bitrate are both defined"); return false; @@ -86,7 +86,7 @@ TwolameEncoder::Configure(const config_param ¶m, Error &error) } else { /* a bit rate was configured */ - value = param.GetBlockValue("bitrate"); + value = block.GetBlockValue("bitrate"); if (value == nullptr) { error.Set(config_domain, "neither bitrate nor quality defined"); @@ -107,15 +107,15 @@ TwolameEncoder::Configure(const config_param ¶m, Error &error) } static Encoder * -twolame_encoder_init(const config_param ¶m, Error &error_r) +twolame_encoder_init(const ConfigBlock &block, Error &error_r) { FormatDebug(twolame_encoder_domain, "libtwolame version %s", get_twolame_version()); TwolameEncoder *encoder = new TwolameEncoder(); - /* load configuration from "param" */ - if (!encoder->Configure(param, error_r)) { + /* load configuration from "block" */ + if (!encoder->Configure(block, error_r)) { /* configuration has failed, roll back and return error */ delete encoder; return nullptr; diff --git a/src/encoder/plugins/TwolameEncoderPlugin.hxx b/src/encoder/plugins/TwolameEncoderPlugin.hxx index 531dd3e90..2c0b250da 100644 --- a/src/encoder/plugins/TwolameEncoderPlugin.hxx +++ b/src/encoder/plugins/TwolameEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/VorbisEncoderPlugin.cxx b/src/encoder/plugins/VorbisEncoderPlugin.cxx index ecc784a47..11e057eaa 100644 --- a/src/encoder/plugins/VorbisEncoderPlugin.cxx +++ b/src/encoder/plugins/VorbisEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,14 +25,13 @@ #include "tag/Tag.hxx" #include "AudioFormat.hxx" #include "config/ConfigError.hxx" +#include "util/StringUtil.hxx" #include "util/NumberParser.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include <vorbis/vorbisenc.h> -#include <glib.h> - struct vorbis_encoder { /** the base class */ Encoder encoder; @@ -58,18 +57,18 @@ struct vorbis_encoder { static constexpr Domain vorbis_encoder_domain("vorbis_encoder"); static bool -vorbis_encoder_configure(struct vorbis_encoder *encoder, - const config_param ¶m, Error &error) +vorbis_encoder_configure(struct vorbis_encoder &encoder, + const ConfigBlock &block, Error &error) { - const char *value = param.GetBlockValue("quality"); + const char *value = block.GetBlockValue("quality"); if (value != nullptr) { /* a quality was configured (VBR) */ char *endptr; - encoder->quality = ParseDouble(value, &endptr); + encoder.quality = ParseDouble(value, &endptr); - if (*endptr != '\0' || encoder->quality < -1.0 || - encoder->quality > 10.0) { + if (*endptr != '\0' || encoder.quality < -1.0 || + encoder.quality > 10.0) { error.Format(config_domain, "quality \"%s\" is not a number in the " "range -1 to 10", @@ -77,7 +76,7 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder, return false; } - if (param.GetBlockValue("bitrate") != nullptr) { + if (block.GetBlockValue("bitrate") != nullptr) { error.Set(config_domain, "quality and bitrate are both defined"); return false; @@ -85,18 +84,18 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder, } else { /* a bit rate was configured */ - value = param.GetBlockValue("bitrate"); + value = block.GetBlockValue("bitrate"); if (value == nullptr) { error.Set(config_domain, "neither bitrate nor quality defined"); return false; } - encoder->quality = -2.0; + encoder.quality = -2.0; char *endptr; - encoder->bitrate = ParseInt(value, &endptr); - if (*endptr != '\0' || encoder->bitrate <= 0) { + encoder.bitrate = ParseInt(value, &endptr); + if (*endptr != '\0' || encoder.bitrate <= 0) { error.Set(config_domain, "bitrate should be a positive integer"); return false; @@ -107,12 +106,12 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder, } static Encoder * -vorbis_encoder_init(const config_param ¶m, Error &error) +vorbis_encoder_init(const ConfigBlock &block, Error &error) { vorbis_encoder *encoder = new vorbis_encoder(); - /* load configuration from "param" */ - if (!vorbis_encoder_configure(encoder, param, error)) { + /* load configuration from "block" */ + if (!vorbis_encoder_configure(*encoder, block, error)) { /* configuration has failed, roll back and return error */ delete encoder; return nullptr; @@ -132,63 +131,63 @@ vorbis_encoder_finish(Encoder *_encoder) } static bool -vorbis_encoder_reinit(struct vorbis_encoder *encoder, Error &error) +vorbis_encoder_reinit(struct vorbis_encoder &encoder, Error &error) { - vorbis_info_init(&encoder->vi); + vorbis_info_init(&encoder.vi); - if (encoder->quality >= -1.0) { + if (encoder.quality >= -1.0) { /* a quality was configured (VBR) */ - if (0 != vorbis_encode_init_vbr(&encoder->vi, - encoder->audio_format.channels, - encoder->audio_format.sample_rate, - encoder->quality * 0.1)) { + if (0 != vorbis_encode_init_vbr(&encoder.vi, + encoder.audio_format.channels, + encoder.audio_format.sample_rate, + encoder.quality * 0.1)) { error.Set(vorbis_encoder_domain, "error initializing vorbis vbr"); - vorbis_info_clear(&encoder->vi); + vorbis_info_clear(&encoder.vi); return false; } } else { /* a bit rate was configured */ - if (0 != vorbis_encode_init(&encoder->vi, - encoder->audio_format.channels, - encoder->audio_format.sample_rate, -1.0, - encoder->bitrate * 1000, -1.0)) { + if (0 != vorbis_encode_init(&encoder.vi, + encoder.audio_format.channels, + encoder.audio_format.sample_rate, -1.0, + encoder.bitrate * 1000, -1.0)) { error.Set(vorbis_encoder_domain, "error initializing vorbis encoder"); - vorbis_info_clear(&encoder->vi); + vorbis_info_clear(&encoder.vi); return false; } } - vorbis_analysis_init(&encoder->vd, &encoder->vi); - vorbis_block_init(&encoder->vd, &encoder->vb); - encoder->stream.Initialize(GenerateOggSerial()); + vorbis_analysis_init(&encoder.vd, &encoder.vi); + vorbis_block_init(&encoder.vd, &encoder.vb); + encoder.stream.Initialize(GenerateOggSerial()); return true; } static void -vorbis_encoder_headerout(struct vorbis_encoder *encoder, vorbis_comment *vc) +vorbis_encoder_headerout(struct vorbis_encoder &encoder, vorbis_comment &vc) { ogg_packet packet, comments, codebooks; - vorbis_analysis_headerout(&encoder->vd, vc, + vorbis_analysis_headerout(&encoder.vd, &vc, &packet, &comments, &codebooks); - encoder->stream.PacketIn(packet); - encoder->stream.PacketIn(comments); - encoder->stream.PacketIn(codebooks); + encoder.stream.PacketIn(packet); + encoder.stream.PacketIn(comments); + encoder.stream.PacketIn(codebooks); } static void -vorbis_encoder_send_header(struct vorbis_encoder *encoder) +vorbis_encoder_send_header(struct vorbis_encoder &encoder) { vorbis_comment vc; vorbis_comment_init(&vc); - vorbis_encoder_headerout(encoder, &vc); + vorbis_encoder_headerout(encoder, vc); vorbis_comment_clear(&vc); } @@ -197,11 +196,11 @@ vorbis_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; audio_format.format = SampleFormat::FLOAT; - encoder->audio_format = audio_format; + encoder.audio_format = audio_format; if (!vorbis_encoder_reinit(encoder, error)) return false; @@ -212,78 +211,78 @@ vorbis_encoder_open(Encoder *_encoder, } static void -vorbis_encoder_clear(struct vorbis_encoder *encoder) +vorbis_encoder_clear(struct vorbis_encoder &encoder) { - encoder->stream.Deinitialize(); - vorbis_block_clear(&encoder->vb); - vorbis_dsp_clear(&encoder->vd); - vorbis_info_clear(&encoder->vi); + encoder.stream.Deinitialize(); + vorbis_block_clear(&encoder.vb); + vorbis_dsp_clear(&encoder.vd); + vorbis_info_clear(&encoder.vi); } static void vorbis_encoder_close(Encoder *_encoder) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; vorbis_encoder_clear(encoder); } static void -vorbis_encoder_blockout(struct vorbis_encoder *encoder) +vorbis_encoder_blockout(struct vorbis_encoder &encoder) { - while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) { - vorbis_analysis(&encoder->vb, nullptr); - vorbis_bitrate_addblock(&encoder->vb); + while (vorbis_analysis_blockout(&encoder.vd, &encoder.vb) == 1) { + vorbis_analysis(&encoder.vb, nullptr); + vorbis_bitrate_addblock(&encoder.vb); ogg_packet packet; - while (vorbis_bitrate_flushpacket(&encoder->vd, &packet)) - encoder->stream.PacketIn(packet); + while (vorbis_bitrate_flushpacket(&encoder.vd, &packet)) + encoder.stream.PacketIn(packet); } } static bool vorbis_encoder_flush(Encoder *_encoder, gcc_unused Error &error) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; - encoder->stream.Flush(); + encoder.stream.Flush(); return true; } static bool vorbis_encoder_pre_tag(Encoder *_encoder, gcc_unused Error &error) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; - vorbis_analysis_wrote(&encoder->vd, 0); + vorbis_analysis_wrote(&encoder.vd, 0); vorbis_encoder_blockout(encoder); /* reinitialize vorbis_dsp_state and vorbis_block to reset the end-of-stream marker */ - vorbis_block_clear(&encoder->vb); - vorbis_dsp_clear(&encoder->vd); - vorbis_analysis_init(&encoder->vd, &encoder->vi); - vorbis_block_init(&encoder->vd, &encoder->vb); + vorbis_block_clear(&encoder.vb); + vorbis_dsp_clear(&encoder.vd); + vorbis_analysis_init(&encoder.vd, &encoder.vi); + vorbis_block_init(&encoder.vd, &encoder.vb); - encoder->stream.Flush(); + encoder.stream.Flush(); return true; } static void -copy_tag_to_vorbis_comment(vorbis_comment *vc, const Tag *tag) +copy_tag_to_vorbis_comment(vorbis_comment *vc, const Tag &tag) { - for (const auto &item : *tag) { - char *name = g_ascii_strup(tag_item_names[item.type], -1); + for (const auto &item : tag) { + char name[64]; + ToUpperASCII(name, tag_item_names[item.type], sizeof(name)); vorbis_comment_add_tag(vc, name, item.value); - g_free(name); } } static bool -vorbis_encoder_tag(Encoder *_encoder, const Tag *tag, +vorbis_encoder_tag(Encoder *_encoder, const Tag &tag, gcc_unused Error &error) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; vorbis_comment comment; /* write the vorbis_comment object */ @@ -293,11 +292,11 @@ vorbis_encoder_tag(Encoder *_encoder, const Tag *tag, /* reset ogg_stream_state and begin a new stream */ - encoder->stream.Reinitialize(GenerateOggSerial()); + encoder.stream.Reinitialize(GenerateOggSerial()); /* send that vorbis_comment to the ogg_stream_state */ - vorbis_encoder_headerout(encoder, &comment); + vorbis_encoder_headerout(encoder, comment); vorbis_comment_clear(&comment); return true; @@ -317,19 +316,19 @@ vorbis_encoder_write(Encoder *_encoder, const void *data, size_t length, gcc_unused Error &error) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; - unsigned num_frames = length / encoder->audio_format.GetFrameSize(); + unsigned num_frames = length / encoder.audio_format.GetFrameSize(); /* this is for only 16-bit audio */ - interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&encoder->vd, + interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&encoder.vd, num_frames), (const float *)data, num_frames, - encoder->audio_format.channels); + encoder.audio_format.channels); - vorbis_analysis_wrote(&encoder->vd, num_frames); + vorbis_analysis_wrote(&encoder.vd, num_frames); vorbis_encoder_blockout(encoder); return true; } @@ -337,9 +336,9 @@ vorbis_encoder_write(Encoder *_encoder, static size_t vorbis_encoder_read(Encoder *_encoder, void *dest, size_t length) { - struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder; + struct vorbis_encoder &encoder = *(struct vorbis_encoder *)_encoder; - return encoder->stream.PageOut(dest, length); + return encoder.stream.PageOut(dest, length); } static const char * diff --git a/src/encoder/plugins/VorbisEncoderPlugin.hxx b/src/encoder/plugins/VorbisEncoderPlugin.hxx index 80703bf88..a6d1c46d2 100644 --- a/src/encoder/plugins/VorbisEncoderPlugin.hxx +++ b/src/encoder/plugins/VorbisEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/encoder/plugins/WaveEncoderPlugin.cxx b/src/encoder/plugins/WaveEncoderPlugin.cxx index 97a26e821..3493bed3a 100644 --- a/src/encoder/plugins/WaveEncoderPlugin.cxx +++ b/src/encoder/plugins/WaveEncoderPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -79,7 +79,7 @@ fill_wave_header(struct wave_header *header, int channels, int bits, } static Encoder * -wave_encoder_init(gcc_unused const config_param ¶m, +wave_encoder_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { WaveEncoder *encoder = new WaveEncoder(); diff --git a/src/encoder/plugins/WaveEncoderPlugin.hxx b/src/encoder/plugins/WaveEncoderPlugin.hxx index 341b98adc..878985612 100644 --- a/src/encoder/plugins/WaveEncoderPlugin.hxx +++ b/src/encoder/plugins/WaveEncoderPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx index 939824baa..40aea6618 100644 --- a/src/event/BufferedSocket.cxx +++ b/src/event/BufferedSocket.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "BufferedSocket.hxx" -#include "system/SocketError.hxx" +#include "net/SocketError.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Compiler.h" diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx index b1882de2f..1478f82ca 100644 --- a/src/event/BufferedSocket.hxx +++ b/src/event/BufferedSocket.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/Call.cxx b/src/event/Call.cxx index bc16c4e95..216d4a5e8 100644 --- a/src/event/Call.cxx +++ b/src/event/Call.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/Call.hxx b/src/event/Call.hxx index 808965de1..f16337ac3 100644 --- a/src/event/Call.hxx +++ b/src/event/Call.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/DeferredMonitor.cxx b/src/event/DeferredMonitor.cxx index 3e824012f..7d23746de 100644 --- a/src/event/DeferredMonitor.cxx +++ b/src/event/DeferredMonitor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/DeferredMonitor.hxx b/src/event/DeferredMonitor.hxx index c4aa605fc..2697a7226 100644 --- a/src/event/DeferredMonitor.hxx +++ b/src/event/DeferredMonitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx index 457add2b0..ba7f7b455 100644 --- a/src/event/FullyBufferedSocket.cxx +++ b/src/event/FullyBufferedSocket.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "FullyBufferedSocket.hxx" -#include "system/SocketError.hxx" +#include "net/SocketError.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Compiler.h" diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx index b03152be2..77b5c4416 100644 --- a/src/event/FullyBufferedSocket.hxx +++ b/src/event/FullyBufferedSocket.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/IdleMonitor.cxx b/src/event/IdleMonitor.cxx index 4af656a22..32b412825 100644 --- a/src/event/IdleMonitor.cxx +++ b/src/event/IdleMonitor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/IdleMonitor.hxx b/src/event/IdleMonitor.hxx index 8d4d2681a..c7e9e4035 100644 --- a/src/event/IdleMonitor.hxx +++ b/src/event/IdleMonitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx index 1bac7c551..fca1f516f 100644 --- a/src/event/Loop.cxx +++ b/src/event/Loop.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx index 56804dc81..4001e31a0 100644 --- a/src/event/Loop.hxx +++ b/src/event/Loop.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx index ef77de425..6fc420b8d 100644 --- a/src/event/MultiSocketMonitor.cxx +++ b/src/event/MultiSocketMonitor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx index b40ee8caa..eb6f51aa8 100644 --- a/src/event/MultiSocketMonitor.hxx +++ b/src/event/MultiSocketMonitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollGroup.hxx b/src/event/PollGroup.hxx index a2f176860..f376d50ca 100644 --- a/src/event/PollGroup.hxx +++ b/src/event/PollGroup.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollGroupEPoll.hxx b/src/event/PollGroupEPoll.hxx index d8edb8a1f..9457823a6 100644 --- a/src/event/PollGroupEPoll.hxx +++ b/src/event/PollGroupEPoll.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollGroupPoll.cxx b/src/event/PollGroupPoll.cxx index 402f8616f..b961c3f90 100644 --- a/src/event/PollGroupPoll.cxx +++ b/src/event/PollGroupPoll.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollGroupPoll.hxx b/src/event/PollGroupPoll.hxx index f7a3ccb4f..8d73280b4 100644 --- a/src/event/PollGroupPoll.hxx +++ b/src/event/PollGroupPoll.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollGroupWinSelect.cxx b/src/event/PollGroupWinSelect.cxx index 26c8abd46..796f699e2 100644 --- a/src/event/PollGroupWinSelect.cxx +++ b/src/event/PollGroupWinSelect.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollGroupWinSelect.hxx b/src/event/PollGroupWinSelect.hxx index d01067709..276a602a6 100644 --- a/src/event/PollGroupWinSelect.hxx +++ b/src/event/PollGroupWinSelect.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/PollResultGeneric.hxx b/src/event/PollResultGeneric.hxx index 35daf7f08..3dbeda0e4 100644 --- a/src/event/PollResultGeneric.hxx +++ b/src/event/PollResultGeneric.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/ServerSocket.cxx b/src/event/ServerSocket.cxx index 313f0a6cf..f82c533c3 100644 --- a/src/event/ServerSocket.cxx +++ b/src/event/ServerSocket.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,10 +19,14 @@ #include "config.h" #include "ServerSocket.hxx" -#include "system/SocketUtil.hxx" -#include "system/SocketError.hxx" +#include "net/StaticSocketAddress.hxx" +#include "net/AllocatedSocketAddress.hxx" +#include "net/SocketAddress.hxx" +#include "net/SocketUtil.hxx" +#include "net/SocketError.hxx" +#include "net/Resolver.hxx" +#include "net/ToString.hxx" #include "event/SocketMonitor.hxx" -#include "system/Resolver.hxx" #include "system/fd_util.h" #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" @@ -52,39 +56,35 @@ #include <netdb.h> #endif -#define DEFAULT_PORT 6600 - class OneServerSocket final : private SocketMonitor { ServerSocket &parent; const unsigned serial; +#ifdef HAVE_UN AllocatedPath path; +#endif - size_t address_length; - struct sockaddr *address; + const AllocatedSocketAddress address; public: + template<typename A> OneServerSocket(EventLoop &_loop, ServerSocket &_parent, unsigned _serial, - const struct sockaddr *_address, - size_t _address_length) + A &&_address) :SocketMonitor(_loop), parent(_parent), serial(_serial), +#ifdef HAVE_UN path(AllocatedPath::Null()), - address_length(_address_length), - address((sockaddr *)xmemdup(_address, _address_length)) +#endif + address(std::forward<A>(_address)) { - assert(_address != nullptr); - assert(_address_length > 0); } OneServerSocket(const OneServerSocket &other) = delete; OneServerSocket &operator=(const OneServerSocket &other) = delete; ~OneServerSocket() { - free(address); - if (IsDefined()) Close(); } @@ -93,11 +93,13 @@ public: return serial; } +#ifdef HAVE_UN void SetPath(AllocatedPath &&_path) { assert(path.IsNull()); path = std::move(_path); } +#endif bool Open(Error &error); @@ -106,7 +108,7 @@ public: gcc_pure std::string ToString() const { - return sockaddr_to_string(address, address_length); + return ::ToString(address); } void SetFD(int _fd) { @@ -150,10 +152,10 @@ get_remote_uid(int fd) inline void OneServerSocket::Accept() { - struct sockaddr_storage peer_address; + StaticSocketAddress peer_address; size_t peer_address_length = sizeof(peer_address); int peer_fd = - accept_cloexec_nonblock(Get(), (struct sockaddr*)&peer_address, + accept_cloexec_nonblock(Get(), peer_address.GetAddress(), &peer_address_length); if (peer_fd < 0) { const SocketErrorMessage msg; @@ -162,6 +164,8 @@ OneServerSocket::Accept() return; } + peer_address.SetSize(peer_address_length); + if (socket_keepalive(peer_fd)) { const SocketErrorMessage msg; FormatError(server_socket_domain, @@ -169,9 +173,8 @@ OneServerSocket::Accept() (const char *)msg); } - parent.OnAccept(peer_fd, - (const sockaddr &)peer_address, - peer_address_length, get_remote_uid(peer_fd)); + parent.OnAccept(peer_fd, peer_address, + get_remote_uid(peer_fd)); } bool @@ -186,19 +189,21 @@ OneServerSocket::Open(Error &error) { assert(!IsDefined()); - int _fd = socket_bind_listen(address->sa_family, + int _fd = socket_bind_listen(address.GetFamily(), SOCK_STREAM, 0, - address, address_length, 5, + address, 5, error); if (_fd < 0) return false; +#ifdef HAVE_UN /* allow everybody to connect */ if (!path.IsNull()) chmod(path.c_str(), 0666); +#endif - /* register in the GLib main loop */ + /* register in the EventLoop */ SetFD(_fd); @@ -282,10 +287,19 @@ ServerSocket::Close() } OneServerSocket & -ServerSocket::AddAddress(const sockaddr &address, size_t address_length) +ServerSocket::AddAddress(SocketAddress address) { sockets.emplace_back(loop, *this, next_serial, - &address, address_length); + address); + + return sockets.back(); +} + +OneServerSocket & +ServerSocket::AddAddress(AllocatedSocketAddress &&address) +{ + sockets.emplace_back(loop, *this, next_serial, + std::move(address)); return sockets.back(); } @@ -295,17 +309,18 @@ ServerSocket::AddFD(int fd, Error &error) { assert(fd >= 0); - struct sockaddr_storage address; + StaticSocketAddress address; socklen_t address_length = sizeof(address); - if (getsockname(fd, (struct sockaddr *)&address, + if (getsockname(fd, address.GetAddress(), &address_length) < 0) { SetSocketError(error); error.AddPrefix("Failed to get socket address: "); return false; } - OneServerSocket &s = AddAddress((const sockaddr &)address, - address_length); + address.SetSize(address_length); + + OneServerSocket &s = AddAddress(address); s.SetFD(fd); return true; @@ -322,7 +337,7 @@ ServerSocket::AddPortIPv4(unsigned port) sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; - AddAddress((const sockaddr &)sin, sizeof(sin)); + AddAddress({(const sockaddr *)&sin, sizeof(sin)}); } #ifdef HAVE_IPV6 @@ -335,7 +350,7 @@ ServerSocket::AddPortIPv6(unsigned port) sin.sin6_port = htons(port); sin.sin6_family = AF_INET6; - AddAddress((const sockaddr &)sin, sizeof(sin)); + AddAddress({(const sockaddr *)&sin, sizeof(sin)}); } /** @@ -394,7 +409,7 @@ ServerSocket::AddHost(const char *hostname, unsigned port, Error &error) return false; for (const struct addrinfo *i = ai; i != nullptr; i = i->ai_next) - AddAddress(*i->ai_addr, i->ai_addrlen); + AddAddress(SocketAddress(i->ai_addr, i->ai_addrlen)); freeaddrinfo(ai); @@ -414,21 +429,14 @@ bool ServerSocket::AddPath(AllocatedPath &&path, Error &error) { #ifdef HAVE_UN - struct sockaddr_un s_un; - - const size_t path_length = path.length(); - if (path_length >= sizeof(s_un.sun_path)) { - error.Set(server_socket_domain, - "UNIX socket path is too long"); - return false; - } + (void)error; RemoveFile(path); - s_un.sun_family = AF_UNIX; - memcpy(s_un.sun_path, path.c_str(), path_length + 1); + AllocatedSocketAddress address; + address.SetLocal(path.c_str()); - OneServerSocket &s = AddAddress((const sockaddr &)s_un, sizeof(s_un)); + OneServerSocket &s = AddAddress(std::move(address)); s.SetPath(std::move(path)); return true; diff --git a/src/event/ServerSocket.hxx b/src/event/ServerSocket.hxx index 4c3fd9f1d..a59ac3f2a 100644 --- a/src/event/ServerSocket.hxx +++ b/src/event/ServerSocket.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,16 +24,11 @@ #include <stddef.h> -struct sockaddr; +class SocketAddress; +class AllocatedSocketAddress; class EventLoop; class Error; class AllocatedPath; - -typedef void (*server_socket_callback_t)(int fd, - const struct sockaddr *address, - size_t address_length, int uid, - void *ctx); - class OneServerSocket; /** @@ -57,7 +52,8 @@ public: } private: - OneServerSocket &AddAddress(const sockaddr &address, size_t length); + OneServerSocket &AddAddress(SocketAddress address); + OneServerSocket &AddAddress(AllocatedSocketAddress &&address); /** * Add a listener on a port on all IPv4 interfaces. @@ -78,8 +74,7 @@ public: * Add a listener on a port on all interfaces. * * @param port the TCP port - * @param error_r location to store the error occurring, or nullptr to - * ignore errors + * @param error location to store the error occurring * @return true on success */ bool AddPort(unsigned port, Error &error); @@ -90,8 +85,7 @@ public: * * @param hostname the host name to be resolved * @param port the TCP port - * @param error_r location to store the error occurring, or nullptr to - * ignore errors + * @param error location to store the error occurring * @return true on success */ bool AddHost(const char *hostname, unsigned port, Error &error); @@ -100,8 +94,7 @@ public: * Add a listener on a Unix domain socket. * * @param path the absolute socket path - * @param error_r location to store the error occurring, or nullptr to - * ignore errors + * @param error location to store the error occurring * @return true on success */ bool AddPath(AllocatedPath &&path, Error &error); @@ -117,8 +110,7 @@ public: void Close(); protected: - virtual void OnAccept(int fd, const sockaddr &address, - size_t address_length, int uid) = 0; + virtual void OnAccept(int fd, SocketAddress address, int uid) = 0; }; #endif diff --git a/src/event/SignalMonitor.cxx b/src/event/SignalMonitor.cxx index 2d8fe681f..c9a7085eb 100644 --- a/src/event/SignalMonitor.cxx +++ b/src/event/SignalMonitor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/SignalMonitor.hxx b/src/event/SignalMonitor.hxx index a41e57ef9..6bed61c66 100644 --- a/src/event/SignalMonitor.hxx +++ b/src/event/SignalMonitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx index 69207287d..00e6e6c45 100644 --- a/src/event/SocketMonitor.cxx +++ b/src/event/SocketMonitor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx index 56d4273f0..4231e3ec0 100644 --- a/src/event/SocketMonitor.hxx +++ b/src/event/SocketMonitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/TimeoutMonitor.cxx b/src/event/TimeoutMonitor.cxx index 007e8aa2c..1bda47c5c 100644 --- a/src/event/TimeoutMonitor.cxx +++ b/src/event/TimeoutMonitor.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/TimeoutMonitor.hxx b/src/event/TimeoutMonitor.hxx index 414d48aa6..00289a0db 100644 --- a/src/event/TimeoutMonitor.hxx +++ b/src/event/TimeoutMonitor.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/event/WakeFD.hxx b/src/event/WakeFD.hxx index c6222b59c..4b3792111 100644 --- a/src/event/WakeFD.hxx +++ b/src/event/WakeFD.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/filter/FilterConfig.cxx b/src/filter/FilterConfig.cxx index d8c1fc6c2..c5f24362c 100644 --- a/src/filter/FilterConfig.cxx +++ b/src/filter/FilterConfig.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,10 +21,11 @@ #include "FilterConfig.hxx" #include "plugins/ChainFilterPlugin.hxx" #include "FilterPlugin.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigOption.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigError.hxx" +#include "config/Block.hxx" #include "util/Error.hxx" #include <algorithm> @@ -34,8 +35,8 @@ static bool filter_chain_append_new(Filter &chain, const char *template_name, Error &error) { - const struct config_param *cfg = - config_find_block(CONF_AUDIO_FILTER, "name", template_name); + const auto *cfg = config_find_block(ConfigBlockOption::AUDIO_FILTER, + "name", template_name); if (cfg == nullptr) { error.Format(config_domain, "filter template not found: %s", diff --git a/src/filter/FilterConfig.hxx b/src/filter/FilterConfig.hxx index 1018eed51..1c6f14f73 100644 --- a/src/filter/FilterConfig.hxx +++ b/src/filter/FilterConfig.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ class Error; * configured filter sections. * @param chain the chain to append filters on * @param spec the filter chain specification - * @param error_r space to return an error description + * @param error space to return an error description * @return true on success */ bool diff --git a/src/filter/FilterInternal.hxx b/src/filter/FilterInternal.hxx index d2e619540..6969e8a18 100644 --- a/src/filter/FilterInternal.hxx +++ b/src/filter/FilterInternal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -38,12 +38,10 @@ public: /** * Opens the filter, preparing it for FilterPCM(). * - * @param filter the filter object * @param af the audio format of incoming data; the * plugin may modify the object to enforce another input * format - * @param error location to store the error occurring, or nullptr - * to ignore errors. + * @param error location to store the error occurring * @return the format of outgoing data or * AudioFormat::Undefined() on error */ @@ -57,10 +55,8 @@ public: /** * Filters a block of PCM data. * - * @param filter the filter object * @param src the input buffer - * @param error location to store the error occurring, or nullptr - * to ignore errors. + * @param error location to store the error occurring * @return the destination buffer on success (will be * invalidated by Close() or FilterPCM()), nullptr on * error diff --git a/src/filter/FilterPlugin.cxx b/src/filter/FilterPlugin.cxx index 98314f771..93d1942a0 100644 --- a/src/filter/FilterPlugin.cxx +++ b/src/filter/FilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #include "config.h" #include "FilterPlugin.hxx" #include "FilterRegistry.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "config/ConfigError.hxx" #include "util/Error.hxx" @@ -28,20 +28,20 @@ Filter * filter_new(const struct filter_plugin *plugin, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { assert(plugin != nullptr); assert(!error.IsDefined()); - return plugin->init(param, error); + return plugin->init(block, error); } Filter * -filter_configured_new(const config_param ¶m, Error &error) +filter_configured_new(const ConfigBlock &block, Error &error) { assert(!error.IsDefined()); - const char *plugin_name = param.GetBlockValue("plugin"); + const char *plugin_name = block.GetBlockValue("plugin"); if (plugin_name == nullptr) { error.Set(config_domain, "No filter plugin specified"); return nullptr; @@ -54,5 +54,5 @@ filter_configured_new(const config_param ¶m, Error &error) return nullptr; } - return filter_new(plugin, param, error); + return filter_new(plugin, block, error); } diff --git a/src/filter/FilterPlugin.hxx b/src/filter/FilterPlugin.hxx index 443d29881..68ad74240 100644 --- a/src/filter/FilterPlugin.hxx +++ b/src/filter/FilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #ifndef MPD_FILTER_PLUGIN_HXX #define MPD_FILTER_PLUGIN_HXX -struct config_param; +struct ConfigBlock; class Filter; class Error; @@ -36,32 +36,32 @@ struct filter_plugin { /** * Allocates and configures a filter. */ - Filter *(*init)(const config_param ¶m, Error &error); + Filter *(*init)(const ConfigBlock &block, Error &error); }; /** * Creates a new instance of the specified filter plugin. * * @param plugin the filter plugin - * @param param optional configuration section + * @param block configuration section * @param error location to store the error occurring, or nullptr to * ignore errors. * @return a new filter object, or nullptr on error */ Filter * filter_new(const struct filter_plugin *plugin, - const config_param ¶m, Error &error); + const ConfigBlock &block, Error &error); /** * Creates a new filter, loads configuration and the plugin name from * the specified configuration section. * - * @param param the configuration section + * @param block the configuration section * @param error location to store the error occurring, or nullptr to * ignore errors. * @return a new filter object, or nullptr on error */ Filter * -filter_configured_new(const config_param ¶m, Error &error); +filter_configured_new(const ConfigBlock &block, Error &error); #endif diff --git a/src/filter/FilterRegistry.cxx b/src/filter/FilterRegistry.cxx index 286fb8db3..8c679940f 100644 --- a/src/filter/FilterRegistry.cxx +++ b/src/filter/FilterRegistry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/filter/FilterRegistry.hxx b/src/filter/FilterRegistry.hxx index 24618a87a..51fac615f 100644 --- a/src/filter/FilterRegistry.hxx +++ b/src/filter/FilterRegistry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/filter/plugins/AutoConvertFilterPlugin.cxx b/src/filter/plugins/AutoConvertFilterPlugin.cxx index 8586cb86e..52918080e 100644 --- a/src/filter/plugins/AutoConvertFilterPlugin.cxx +++ b/src/filter/plugins/AutoConvertFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,7 @@ #include "filter/FilterInternal.hxx" #include "filter/FilterRegistry.hxx" #include "AudioFormat.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "util/ConstBuffer.hxx" #include <assert.h> @@ -70,7 +70,7 @@ AutoConvertFilter::Open(AudioFormat &in_audio_format, Error &error) if (in_audio_format != child_audio_format) { /* yes - create a convert_filter */ - const config_param empty; + const ConfigBlock empty; convert = filter_new(&convert_filter_plugin, empty, error); if (convert == nullptr) { filter->Close(); diff --git a/src/filter/plugins/AutoConvertFilterPlugin.hxx b/src/filter/plugins/AutoConvertFilterPlugin.hxx index c5dfdd2f6..1b9061331 100644 --- a/src/filter/plugins/AutoConvertFilterPlugin.hxx +++ b/src/filter/plugins/AutoConvertFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/filter/plugins/ChainFilterPlugin.cxx b/src/filter/plugins/ChainFilterPlugin.cxx index 4aeee69af..b965295f7 100644 --- a/src/filter/plugins/ChainFilterPlugin.cxx +++ b/src/filter/plugins/ChainFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -70,7 +70,7 @@ private: static constexpr Domain chain_filter_domain("chain_filter"); static Filter * -chain_filter_init(gcc_unused const config_param ¶m, +chain_filter_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new ChainFilter(); diff --git a/src/filter/plugins/ChainFilterPlugin.hxx b/src/filter/plugins/ChainFilterPlugin.hxx index b36aa3322..8f346e178 100644 --- a/src/filter/plugins/ChainFilterPlugin.hxx +++ b/src/filter/plugins/ChainFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ class Filter; * Creates a new filter chain. */ Filter * -filter_chain_new(void); +filter_chain_new(); /** * Appends a new filter at the end of the filter chain. You must call diff --git a/src/filter/plugins/ConvertFilterPlugin.cxx b/src/filter/plugins/ConvertFilterPlugin.cxx index 5c6a07ba1..18053abd0 100644 --- a/src/filter/plugins/ConvertFilterPlugin.cxx +++ b/src/filter/plugins/ConvertFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -59,7 +59,7 @@ public: }; static Filter * -convert_filter_init(gcc_unused const config_param ¶m, +convert_filter_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new ConvertFilter(); diff --git a/src/filter/plugins/ConvertFilterPlugin.hxx b/src/filter/plugins/ConvertFilterPlugin.hxx index bb4673651..8dd0bcd8e 100644 --- a/src/filter/plugins/ConvertFilterPlugin.hxx +++ b/src/filter/plugins/ConvertFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/filter/plugins/NormalizeFilterPlugin.cxx b/src/filter/plugins/NormalizeFilterPlugin.cxx index 372ab53ac..77e8c1535 100644 --- a/src/filter/plugins/NormalizeFilterPlugin.cxx +++ b/src/filter/plugins/NormalizeFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -42,7 +42,7 @@ public: }; static Filter * -normalize_filter_init(gcc_unused const config_param ¶m, +normalize_filter_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new NormalizeFilter(); diff --git a/src/filter/plugins/NullFilterPlugin.cxx b/src/filter/plugins/NullFilterPlugin.cxx index ebd8e4ec5..0bd8170f0 100644 --- a/src/filter/plugins/NullFilterPlugin.cxx +++ b/src/filter/plugins/NullFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -48,7 +48,7 @@ public: }; static Filter * -null_filter_init(gcc_unused const config_param ¶m, +null_filter_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new NullFilter(); diff --git a/src/filter/plugins/ReplayGainFilterPlugin.cxx b/src/filter/plugins/ReplayGainFilterPlugin.cxx index f76e48e37..ca1119331 100644 --- a/src/filter/plugins/ReplayGainFilterPlugin.cxx +++ b/src/filter/plugins/ReplayGainFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -150,7 +150,7 @@ ReplayGainFilter::Update() } static Filter * -replay_gain_filter_init(gcc_unused const config_param ¶m, +replay_gain_filter_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new ReplayGainFilter(); diff --git a/src/filter/plugins/ReplayGainFilterPlugin.hxx b/src/filter/plugins/ReplayGainFilterPlugin.hxx index 346541b97..ec0cafc60 100644 --- a/src/filter/plugins/ReplayGainFilterPlugin.hxx +++ b/src/filter/plugins/ReplayGainFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -38,9 +38,9 @@ replay_gain_filter_set_mixer(Filter *_filter, Mixer *mixer, unsigned base); /** - * Sets a new #replay_gain_info at the beginning of a new song. + * Sets a new #ReplayGainInfo at the beginning of a new song. * - * @param info the new #replay_gain_info value, or nullptr if no replay + * @param info the new #ReplayGainInfo value, or nullptr if no replay * gain data is available for the current song */ void diff --git a/src/filter/plugins/RouteFilterPlugin.cxx b/src/filter/plugins/RouteFilterPlugin.cxx index 4094119f2..11eb534c9 100644 --- a/src/filter/plugins/RouteFilterPlugin.cxx +++ b/src/filter/plugins/RouteFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,7 +41,7 @@ #include "config.h" #include "config/ConfigError.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "AudioFormat.hxx" #include "filter/FilterPlugin.hxx" #include "filter/FilterInternal.hxx" @@ -114,11 +114,11 @@ public: * a>b, c>d, e>f, ... * where a... are non-unique, non-negative integers * and input channel a gets copied to output channel b, etc. - * @param param the configuration block to read + * @param block the configuration block to read * @param filter a route_filter whose min_channels and sources[] to set * @return true on success, false on error */ - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); /* virtual methods from class Filter */ AudioFormat Open(AudioFormat &af, Error &error) override; @@ -128,7 +128,7 @@ public: }; bool -RouteFilter::Configure(const config_param ¶m, Error &error) { +RouteFilter::Configure(const ConfigBlock &block, Error &error) { /* TODO: * With a more clever way of marking "don't copy to output N", @@ -142,7 +142,7 @@ RouteFilter::Configure(const config_param ¶m, Error &error) { min_output_channels = 0; // A cowardly default, just passthrough stereo - const char *routes = param.GetBlockValue("routes", "0>0, 1>1"); + const char *routes = block.GetBlockValue("routes", "0>0, 1>1"); while (true) { routes = StripLeft(routes); @@ -205,10 +205,10 @@ RouteFilter::Configure(const config_param ¶m, Error &error) { } static Filter * -route_filter_init(const config_param ¶m, Error &error) +route_filter_init(const ConfigBlock &block, Error &error) { RouteFilter *filter = new RouteFilter(); - if (!filter->Configure(param, error)) { + if (!filter->Configure(block, error)) { delete filter; return nullptr; } diff --git a/src/filter/plugins/VolumeFilterPlugin.cxx b/src/filter/plugins/VolumeFilterPlugin.cxx index 39188da00..4a7abb953 100644 --- a/src/filter/plugins/VolumeFilterPlugin.cxx +++ b/src/filter/plugins/VolumeFilterPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -50,7 +50,7 @@ public: }; static Filter * -volume_filter_init(gcc_unused const config_param ¶m, +volume_filter_init(gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new VolumeFilter(); diff --git a/src/filter/plugins/VolumeFilterPlugin.hxx b/src/filter/plugins/VolumeFilterPlugin.hxx index b5317dc6f..93191b2d3 100644 --- a/src/filter/plugins/VolumeFilterPlugin.hxx +++ b/src/filter/plugins/VolumeFilterPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/AllocatedPath.cxx b/src/fs/AllocatedPath.cxx index ceaad73ea..8b03ed2f1 100644 --- a/src/fs/AllocatedPath.cxx +++ b/src/fs/AllocatedPath.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,33 +24,14 @@ #include "util/Error.hxx" #include "Compiler.h" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - -#include <string.h> - -#ifdef HAVE_GLIB - -inline AllocatedPath::AllocatedPath(Donate, pointer _value) - :value(_value) { - g_free(_value); -} - -#endif - /* no inlining, please */ AllocatedPath::~AllocatedPath() {} AllocatedPath AllocatedPath::FromUTF8(const char *path_utf8) { -#ifdef HAVE_GLIB - char *path = ::PathFromUTF8(path_utf8); - if (path == nullptr) - return AllocatedPath::Null(); - - return AllocatedPath(Donate(), path); +#if defined(HAVE_FS_CHARSET) || defined(WIN32) + return AllocatedPath(::PathFromUTF8(path_utf8)); #else return FromFS(path_utf8); #endif @@ -80,38 +61,16 @@ AllocatedPath::ToUTF8() const return ::PathToUTF8(c_str()); } -const char * -AllocatedPath::RelativeFS(const char *other_fs) const -{ - const size_t l = length(); - if (memcmp(data(), other_fs, l) != 0) - return nullptr; - - other_fs += l; - if (*other_fs != 0) { - if (!PathTraitsFS::IsSeparator(*other_fs)) - /* mismatch */ - return nullptr; - - /* skip remaining path separators */ - do { - ++other_fs; - } while (PathTraitsFS::IsSeparator(*other_fs)); - } - - return other_fs; -} - void AllocatedPath::ChopSeparators() { size_t l = length(); - const char *p = data(); + const auto *p = data(); while (l >= 2 && PathTraitsFS::IsSeparator(p[l - 1])) { --l; -#if GCC_CHECK_VERSION(4,7) && !defined(__clang__) +#if GCC_CHECK_VERSION(4,7) value.pop_back(); #else value.erase(value.end() - 1, value.end()); diff --git a/src/fs/AllocatedPath.hxx b/src/fs/AllocatedPath.hxx index c345470c8..e733c00a9 100644 --- a/src/fs/AllocatedPath.hxx +++ b/src/fs/AllocatedPath.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "Traits.hxx" #include "Path.hxx" +#include <cstddef> #include <utility> #include <string> @@ -44,13 +45,7 @@ class AllocatedPath { string value; - struct Donate {}; - - /** - * Donate the allocated pointer to a new #AllocatedPath object. - */ - AllocatedPath(Donate, pointer _value); - + AllocatedPath(std::nullptr_t):value() {} AllocatedPath(const_pointer _value):value(_value) {} AllocatedPath(string &&_value):value(std::move(_value)) {} @@ -82,7 +77,7 @@ public: */ gcc_const static AllocatedPath Null() { - return AllocatedPath(""); + return AllocatedPath(nullptr); } gcc_pure @@ -169,11 +164,21 @@ public: return *this; } + gcc_pure + bool operator==(const AllocatedPath &other) const { + return value == other.value; + } + + gcc_pure + bool operator!=(const AllocatedPath &other) const { + return value != other.value; + } + /** * Allows the caller to "steal" the internal value by * providing a rvalue reference to the std::string attribute. */ - std::string &&Steal() { + string &&Steal() { return std::move(value); } @@ -244,7 +249,9 @@ public: * nullptr on mismatch. */ gcc_pure - const char *RelativeFS(const char *other_fs) const; + const_pointer Relative(Path other_fs) const { + return PathTraitsFS::Relative(c_str(), other_fs.c_str()); + } /** * Chop trailing directory separators. @@ -252,7 +259,7 @@ public: void ChopSeparators(); gcc_pure - bool IsAbsolute() { + bool IsAbsolute() const { return PathTraitsFS::IsAbsolute(c_str()); } }; diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx index c634c9340..f0fc1063c 100644 --- a/src/fs/Charset.cxx +++ b/src/fs/Charset.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,12 +21,14 @@ #include "Charset.hxx" #include "Domain.hxx" #include "Limits.hxx" -#include "system/FatalError.hxx" #include "Log.hxx" -#include "Traits.hxx" +#include "lib/icu/Converter.hxx" +#include "util/Error.hxx" +#include "util/AllocatedString.hxx" -#ifdef HAVE_GLIB -#include <glib.h> +#ifdef WIN32 +#include "lib/icu/Win32.hxx" +#include <windows.h> #endif #include <algorithm> @@ -34,130 +36,121 @@ #include <assert.h> #include <string.h> -#ifdef HAVE_GLIB - -/** - * Maximal number of bytes required to represent path name in UTF-8 - * (including nul-terminator). - * This value is a rought estimate of upper bound. - * It's based on path name limit in bytes (MPD_PATH_MAX) - * and assumption that some weird encoding could represent some UTF-8 4 byte - * sequences with single byte. - */ -static constexpr size_t MPD_PATH_MAX_UTF8 = (MPD_PATH_MAX - 1) * 4 + 1; +#ifdef HAVE_FS_CHARSET static std::string fs_charset; -gcc_pure -static bool -IsSupportedCharset(const char *charset) -{ - /* convert a space to check if the charset is valid */ - char *test = g_convert(" ", 1, charset, "UTF-8", nullptr, nullptr, nullptr); - if (test == nullptr) - return false; +static IcuConverter *fs_converter; - g_free(test); - return true; -} - -void -SetFSCharset(const char *charset) +bool +SetFSCharset(const char *charset, Error &error) { assert(charset != nullptr); + assert(fs_converter == nullptr); - if (!IsSupportedCharset(charset)) - FormatFatalError("invalid filesystem charset: %s", charset); - - fs_charset = charset; + fs_converter = IcuConverter::Create(charset, error); + if (fs_converter == nullptr) + return false; FormatDebug(path_domain, "SetFSCharset: fs charset is: %s", fs_charset.c_str()); + return true; } #endif +void +DeinitFSCharset() +{ +#ifdef HAVE_ICU_CONVERTER + delete fs_converter; + fs_converter = nullptr; +#endif +} + const char * GetFSCharset() { -#ifdef HAVE_GLIB +#ifdef HAVE_FS_CHARSET return fs_charset.empty() ? "UTF-8" : fs_charset.c_str(); +#elif defined(WIN32) + return "ACP"; #else return "UTF-8"; #endif } -static inline void FixSeparators(std::string &s) +static inline PathTraitsUTF8::string && +FixSeparators(PathTraitsUTF8::string &&s) { -#ifdef WIN32 // For whatever reason GCC can't convert constexpr to value reference. // This leads to link errors when passing separators directly. - auto from = PathTraitsFS::SEPARATOR; auto to = PathTraitsUTF8::SEPARATOR; - std::replace(s.begin(), s.end(), from, to); -#else - (void)s; -#endif + decltype(to) from = PathTraitsFS::SEPARATOR; + + if (from != to) + /* convert backslash to slash on WIN32 */ + std::replace(s.begin(), s.end(), from, to); + + return std::move(s); } -std::string -PathToUTF8(const char *path_fs) +PathTraitsUTF8::string +PathToUTF8(PathTraitsFS::const_pointer path_fs) { #if !CLANG_CHECK_VERSION(3,6) /* disabled on clang due to -Wtautological-pointer-compare */ assert(path_fs != nullptr); #endif -#ifdef HAVE_GLIB - if (fs_charset.empty()) { +#ifdef WIN32 + const auto buffer = WideCharToMultiByte(CP_UTF8, path_fs); + if (buffer.IsNull()) + return PathTraitsUTF8::string(); + + return FixSeparators(PathTraitsUTF8::string(buffer.c_str())); +#else +#ifdef HAVE_FS_CHARSET + if (fs_converter == nullptr) +#endif + return FixSeparators(path_fs); +#ifdef HAVE_FS_CHARSET + + const auto buffer = fs_converter->ToUTF8(path_fs); + if (buffer.IsNull()) + return PathTraitsUTF8::string(); + + return FixSeparators(PathTraitsUTF8::string(buffer.c_str())); #endif - auto result = std::string(path_fs); - FixSeparators(result); - return result; -#ifdef HAVE_GLIB - } - - GIConv conv = g_iconv_open("utf-8", fs_charset.c_str()); - if (conv == reinterpret_cast<GIConv>(-1)) - return std::string(); - - // g_iconv() does not need nul-terminator, - // std::string could be created without it too. - char path_utf8[MPD_PATH_MAX_UTF8 - 1]; - char *in = const_cast<char *>(path_fs); - char *out = path_utf8; - size_t in_left = strlen(path_fs); - size_t out_left = sizeof(path_utf8); - - size_t ret = g_iconv(conv, &in, &in_left, &out, &out_left); - - g_iconv_close(conv); - - if (ret == static_cast<size_t>(-1) || in_left > 0) - return std::string(); - - auto result_path = std::string(path_utf8, sizeof(path_utf8) - out_left); - FixSeparators(result_path); - return result_path; #endif } -#ifdef HAVE_GLIB +#if defined(HAVE_FS_CHARSET) || defined(WIN32) -char * -PathFromUTF8(const char *path_utf8) +PathTraitsFS::string +PathFromUTF8(PathTraitsUTF8::const_pointer path_utf8) { #if !CLANG_CHECK_VERSION(3,6) /* disabled on clang due to -Wtautological-pointer-compare */ assert(path_utf8 != nullptr); #endif - if (fs_charset.empty()) - return g_strdup(path_utf8); +#ifdef WIN32 + const auto buffer = MultiByteToWideChar(CP_UTF8, path_utf8); + if (buffer.IsNull()) + return PathTraitsFS::string(); + + return PathTraitsFS::string(buffer.c_str()); +#else + if (fs_converter == nullptr) + return path_utf8; + + const auto buffer = fs_converter->FromUTF8(path_utf8); + if (buffer.IsNull()) + return PathTraitsFS::string(); - return g_convert(path_utf8, -1, - fs_charset.c_str(), "utf-8", - nullptr, nullptr, nullptr); + return PathTraitsFS::string(buffer.c_str()); +#endif } #endif diff --git a/src/fs/Charset.hxx b/src/fs/Charset.hxx index 0a71d7c58..7ef19eba3 100644 --- a/src/fs/Charset.hxx +++ b/src/fs/Charset.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,8 +22,13 @@ #include "check.h" #include "Compiler.h" +#include "Traits.hxx" -#include <string> +#if (defined(HAVE_ICU) || defined(HAVE_ICONV)) && !defined(WIN32) +#define HAVE_FS_CHARSET +#endif + +class Error; /** * Gets file system character set name. @@ -32,19 +37,26 @@ gcc_const const char * GetFSCharset(); +bool +SetFSCharset(const char *charset, Error &error); + void -SetFSCharset(const char *charset); +DeinitFSCharset(); /** * Convert the path to UTF-8. * Returns empty string on error. */ gcc_pure gcc_nonnull_all -std::string -PathToUTF8(const char *path_fs); +PathTraitsUTF8::string +PathToUTF8(PathTraitsFS::const_pointer path_fs); -gcc_malloc gcc_nonnull_all -char * -PathFromUTF8(const char *path_utf8); +/** + * Convert the path from UTF-8. + * Returns empty string on error. + */ +gcc_pure gcc_nonnull_all +PathTraitsFS::string +PathFromUTF8(PathTraitsUTF8::const_pointer path_utf8); #endif diff --git a/src/fs/CheckFile.cxx b/src/fs/CheckFile.cxx index a35443674..e900fe9af 100644 --- a/src/fs/CheckFile.cxx +++ b/src/fs/CheckFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,7 @@ #include "CheckFile.hxx" #include "Log.hxx" #include "config/ConfigError.hxx" -#include "FileSystem.hxx" +#include "FileInfo.hxx" #include "Path.hxx" #include "AllocatedPath.hxx" #include "DirectoryReader.hxx" @@ -32,31 +32,37 @@ void CheckDirectoryReadable(Path path_fs) { - struct stat st; - if (!StatFile(path_fs, st)) { - FormatErrno(config_domain, - "Failed to stat directory \"%s\"", - path_fs.c_str()); + Error error; + + FileInfo fi; + if (!GetFileInfo(path_fs, fi, error)) { + LogError(error); return; } - if (!S_ISDIR(st.st_mode)) { + if (!fi.IsDirectory()) { + const auto path_utf8 = path_fs.ToUTF8(); FormatError(config_domain, - "Not a directory: %s", path_fs.c_str()); + "Not a directory: %s", path_utf8.c_str()); return; } #ifndef WIN32 - const auto x = AllocatedPath::Build(path_fs, "."); - if (!StatFile(x, st) && errno == EACCES) + const auto x = AllocatedPath::Build(path_fs, + PathTraitsFS::CURRENT_DIRECTORY); + if (!GetFileInfo(x, fi) && errno == EACCES) { + const auto path_utf8 = path_fs.ToUTF8(); FormatError(config_domain, "No permission to traverse (\"execute\") directory: %s", - path_fs.c_str()); + path_utf8.c_str()); + } #endif const DirectoryReader reader(path_fs); - if (reader.HasFailed() && errno == EACCES) + if (reader.HasFailed() && errno == EACCES) { + const auto path_utf8 = path_fs.ToUTF8(); FormatError(config_domain, - "No permission to read directory: %s", path_fs.c_str()); - + "No permission to read directory: %s", + path_utf8.c_str()); + } } diff --git a/src/fs/CheckFile.hxx b/src/fs/CheckFile.hxx index 00559647d..52fe45f0a 100644 --- a/src/fs/CheckFile.hxx +++ b/src/fs/CheckFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/Config.cxx b/src/fs/Config.cxx index 6aa23005c..0b39a362f 100644 --- a/src/fs/Config.cxx +++ b/src/fs/Config.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,40 +22,22 @@ #include "Charset.hxx" #include "config/ConfigGlobal.hxx" -#ifdef WIN32 -#include <windows.h> // for GetACP() -#include <stdio.h> // for sprintf() -#elif defined(HAVE_GLIB) -#include <glib.h> -#endif - -void -ConfigureFS() +bool +ConfigureFS(Error &error) { -#if defined(HAVE_GLIB) || defined(WIN32) - const char *charset = nullptr; - - charset = config_get_string(CONF_FS_CHARSET, nullptr); - if (charset == nullptr) { -#ifndef WIN32 - const gchar **encodings; - g_get_filename_charsets(&encodings); - - if (encodings[0] != nullptr && *encodings[0] != '\0') - charset = encodings[0]; +#ifdef HAVE_FS_CHARSET + const char *charset = config_get_string(ConfigOption::FS_CHARSET); + return charset == nullptr || SetFSCharset(charset, error); #else - /* Glib claims that file system encoding is always utf-8 - * on native Win32 (i.e. not Cygwin). - * However this is true only if <gstdio.h> helpers are used. - * MPD uses regular <stdio.h> functions. - * Those functions use encoding determined by GetACP(). */ - static char win_charset[13]; - sprintf(win_charset, "cp%u", GetACP()); - charset = win_charset; + (void)error; + return true; #endif - } +} - if (charset != nullptr) - SetFSCharset(charset); +void +DeinitFS() +{ +#ifdef HAVE_FS_CHARSET + DeinitFSCharset(); #endif } diff --git a/src/fs/Config.hxx b/src/fs/Config.hxx index d4f1709f5..1db710551 100644 --- a/src/fs/Config.hxx +++ b/src/fs/Config.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,10 +22,15 @@ #include "check.h" +class Error; + /** * Performs global one-time initialization of this class. */ +bool +ConfigureFS(Error &error); + void -ConfigureFS(); +DeinitFS(); #endif diff --git a/src/fs/DirectoryReader.hxx b/src/fs/DirectoryReader.hxx index f77c0629f..ce27d2b9b 100644 --- a/src/fs/DirectoryReader.hxx +++ b/src/fs/DirectoryReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,89 @@ #include "check.h" #include "Path.hxx" +#ifdef WIN32 + +#include <windows.h> +#include <tchar.h> + +/** + * Reader for directory entries. + */ +class DirectoryReader { + const HANDLE handle; + WIN32_FIND_DATA data; + bool first; + + class MakeWildcardPath { + PathTraitsFS::pointer path; + + public: + MakeWildcardPath(PathTraitsFS::const_pointer _path) { + auto l = _tcslen(_path); + path = new PathTraitsFS::value_type[l + 3]; + _tcscpy(path, _path); + path[l] = _T('\\'); + path[l + 1] = _T('*'); + path[l + 2] = 0; + } + + ~MakeWildcardPath() { + delete[] path; + } + + operator PathTraitsFS::const_pointer() const { + return path; + } + }; + +public: + /** + * Creates new directory reader for the specified #dir. + */ + explicit DirectoryReader(Path dir) + :handle(FindFirstFile(MakeWildcardPath(dir.c_str()), &data)), + first(true) {} + + DirectoryReader(const DirectoryReader &other) = delete; + DirectoryReader &operator=(const DirectoryReader &other) = delete; + + /** + * Destroys this instance. + */ + ~DirectoryReader() { + if (!HasFailed()) + FindClose(handle); + } + + /** + * Checks if directory failed to open. + */ + bool HasFailed() const { + return handle == INVALID_HANDLE_VALUE; + } + + /** + * Reads next directory entry. + */ + bool ReadEntry() { + if (first) { + first = false; + return true; + } + + return FindNextFile(handle, &data) != 0; + } + + /** + * Extracts directory entry that was previously read by #ReadEntry. + */ + Path GetEntry() const { + return Path::FromFS(data.cFileName); + } +}; + +#else + #include <dirent.h> /** @@ -85,3 +168,5 @@ public: }; #endif + +#endif diff --git a/src/fs/Domain.cxx b/src/fs/Domain.cxx index 4f3129219..d278ba1bf 100644 --- a/src/fs/Domain.cxx +++ b/src/fs/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/Domain.hxx b/src/fs/Domain.hxx index 1fd17b37f..77ca64549 100644 --- a/src/fs/Domain.hxx +++ b/src/fs/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/FileInfo.hxx b/src/fs/FileInfo.hxx new file mode 100644 index 000000000..7b272568f --- /dev/null +++ b/src/fs/FileInfo.hxx @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2003-2015 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_FS_FILE_INFO_HXX +#define MPD_FS_FILE_INFO_HXX + +#include "check.h" +#include "Path.hxx" +#include "util/Error.hxx" + +#include <stdint.h> + +#ifdef WIN32 +#include <fileapi.h> +#else +#include <sys/stat.h> +#endif + +#ifdef WIN32 + +static inline constexpr uint64_t +ConstructUint64(DWORD lo, DWORD hi) +{ + return uint64_t(lo) | (uint64_t(hi) << 32); +} + +static constexpr time_t +FileTimeToTimeT(FILETIME ft) +{ + return (ConstructUint64(ft.dwLowDateTime, ft.dwHighDateTime) + - 116444736000000000) / 10000000; +} + +#endif + +class FileInfo { + friend bool GetFileInfo(Path path, FileInfo &info, + bool follow_symlinks); + friend bool GetFileInfo(Path path, FileInfo &info, + Error &error); + friend class FileReader; + +#ifdef WIN32 + WIN32_FILE_ATTRIBUTE_DATA data; +#else + struct stat st; +#endif + +public: + bool IsRegular() const { +#ifdef WIN32 + return (data.dwFileAttributes & + (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) == 0; +#else + return S_ISREG(st.st_mode); +#endif + } + + bool IsDirectory() const { +#ifdef WIN32 + return data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; +#else + return S_ISDIR(st.st_mode); +#endif + } + + uint64_t GetSize() const { +#ifdef WIN32 + return ConstructUint64(data.nFileSizeLow, data.nFileSizeHigh); +#else + return st.st_size; +#endif + } + + time_t GetModificationTime() const { +#ifdef WIN32 + return FileTimeToTimeT(data.ftLastWriteTime); +#else + return st.st_mtime; +#endif + } + +#ifndef WIN32 + uid_t GetUid() const { + return st.st_uid; + } + + mode_t GetMode() const { + return st.st_mode; + } + + dev_t GetDevice() const { + return st.st_dev; + } + + ino_t GetInode() const { + return st.st_ino; + } +#endif +}; + +inline bool +GetFileInfo(Path path, FileInfo &info, bool follow_symlinks=true) +{ +#ifdef WIN32 + (void)follow_symlinks; + return GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, + &info.data); +#else + int ret = follow_symlinks + ? stat(path.c_str(), &info.st) + : lstat(path.c_str(), &info.st); + return ret == 0; +#endif +} + +inline bool +GetFileInfo(Path path, FileInfo &info, bool follow_symlinks, Error &error) +{ + bool success = GetFileInfo(path, info, follow_symlinks); + if (!success) { + const auto path_utf8 = path.ToUTF8(); +#ifdef WIN32 + error.FormatLastError("Failed to access %s", + path_utf8.c_str()); +#else + error.FormatErrno("Failed to access %s", path_utf8.c_str()); +#endif + } + + return success; +} + +inline bool +GetFileInfo(Path path, FileInfo &info, Error &error) +{ + return GetFileInfo(path, info, true, error); +} + +#endif diff --git a/src/fs/FileSystem.cxx b/src/fs/FileSystem.cxx index 4e7c87415..554915b61 100644 --- a/src/fs/FileSystem.cxx +++ b/src/fs/FileSystem.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/FileSystem.hxx b/src/fs/FileSystem.hxx index 4dbb064cb..309c0cdf6 100644 --- a/src/fs/FileSystem.hxx +++ b/src/fs/FileSystem.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,42 +26,27 @@ #include "Path.hxx" +#ifdef WIN32 +#include <fileapi.h> +#endif + #include <sys/stat.h> #include <unistd.h> #include <stdio.h> + class AllocatedPath; namespace FOpenMode { /** - * Open mode for reading text files. - */ - constexpr PathTraitsFS::const_pointer ReadText = "r"; - - /** - * Open mode for reading binary files. - */ - constexpr PathTraitsFS::const_pointer ReadBinary = "rb"; - - /** * Open mode for writing text files. */ - constexpr PathTraitsFS::const_pointer WriteText = "w"; - - /** - * Open mode for writing binary files. - */ - constexpr PathTraitsFS::const_pointer WriteBinary = "wb"; + constexpr PathTraitsFS::const_pointer WriteText = PATH_LITERAL("w"); /** * Open mode for appending text files. */ - constexpr PathTraitsFS::const_pointer AppendText = "a"; - - /** - * Open mode for appending binary files. - */ - constexpr PathTraitsFS::const_pointer AppendBinary = "ab"; + constexpr PathTraitsFS::const_pointer AppendText = PATH_LITERAL("a"); } /** @@ -70,7 +55,11 @@ namespace FOpenMode { static inline FILE * FOpen(Path file, PathTraitsFS::const_pointer mode) { +#ifdef WIN32 + return _tfopen(file.c_str(), mode); +#else return fopen(file.c_str(), mode); +#endif } /** @@ -79,7 +68,11 @@ FOpen(Path file, PathTraitsFS::const_pointer mode) static inline int OpenFile(Path file, int flags, int mode) { +#ifdef WIN32 + return _topen(file.c_str(), flags, mode); +#else return open_cloexec(file.c_str(), flags, mode); +#endif } /** @@ -88,33 +81,40 @@ OpenFile(Path file, int flags, int mode) static inline bool RenameFile(Path oldpath, Path newpath) { +#ifdef WIN32 + return _trename(oldpath.c_str(), newpath.c_str()) == 0; +#else return rename(oldpath.c_str(), newpath.c_str()) == 0; +#endif } +#ifndef WIN32 + /** * Wrapper for stat() that uses #Path names. */ static inline bool StatFile(Path file, struct stat &buf, bool follow_symlinks = true) { -#ifdef WIN32 - (void)follow_symlinks; - return stat(file.c_str(), &buf) == 0; -#else int ret = follow_symlinks ? stat(file.c_str(), &buf) : lstat(file.c_str(), &buf); return ret == 0; -#endif } +#endif + /** * Wrapper for unlink() that uses #Path names. */ static inline bool RemoveFile(Path file) { +#ifdef WIN32 + return _tunlink(file.c_str()) == 0; +#else return unlink(file.c_str()) == 0; +#endif } /** @@ -143,27 +143,21 @@ CheckAccess(Path path, int mode) #endif /** - * Checks is specified path exists and accessible. - */ -static inline bool -CheckAccess(Path path) -{ -#ifdef WIN32 - struct stat buf; - return StatFile(path, buf); -#else - return CheckAccess(path, F_OK); -#endif -} - -/** * Checks if #Path exists and is a regular file. */ static inline bool FileExists(Path path, bool follow_symlinks = true) { +#ifdef WIN32 + (void)follow_symlinks; + + const auto a = GetFileAttributes(path.c_str()); + return a != INVALID_FILE_ATTRIBUTES && + (a & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) == 0; +#else struct stat buf; return StatFile(path, buf, follow_symlinks) && S_ISREG(buf.st_mode); +#endif } /** @@ -172,18 +166,28 @@ FileExists(Path path, bool follow_symlinks = true) static inline bool DirectoryExists(Path path, bool follow_symlinks = true) { +#ifdef WIN32 + (void)follow_symlinks; + + const auto a = GetFileAttributes(path.c_str()); + return a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_DIRECTORY); +#else struct stat buf; return StatFile(path, buf, follow_symlinks) && S_ISDIR(buf.st_mode); +#endif } /** * Checks if #Path exists. */ static inline bool -PathExists(Path path, bool follow_symlinks = true) +PathExists(Path path) { - struct stat buf; - return StatFile(path, buf, follow_symlinks); +#ifdef WIN32 + return GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES; +#else + return CheckAccess(path, F_OK); +#endif } #endif diff --git a/src/fs/Glob.hxx b/src/fs/Glob.hxx new file mode 100644 index 000000000..822cc3fb4 --- /dev/null +++ b/src/fs/Glob.hxx @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003-2015 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_FS_GLOB_XX +#define MPD_FS_GLOB_XX + +#include "check.h" + +#ifdef HAVE_FNMATCH +#define HAVE_CLASS_GLOB +#include <string> +#include <fnmatch.h> +#elif defined(WIN32) +#define HAVE_CLASS_GLOB +#include <string> +#include <shlwapi.h> +#endif + +#ifdef HAVE_CLASS_GLOB +#include "Compiler.h" + +/** + * A pattern that matches file names. It may contain shell wildcards + * (asterisk and question mark). + */ +class Glob { +#if defined(HAVE_FNMATCH) || defined(WIN32) + std::string pattern; +#endif + +public: +#if defined(HAVE_FNMATCH) || defined(WIN32) + explicit Glob(const char *_pattern) + :pattern(_pattern) {} + + Glob(Glob &&other) + :pattern(std::move(other.pattern)) {} +#endif + + gcc_pure + bool Check(const char *name_fs) const { +#ifdef HAVE_FNMATCH + return fnmatch(pattern.c_str(), name_fs, 0) == 0; +#elif defined(WIN32) + return PathMatchSpecA(name_fs, pattern.c_str()); +#endif + } +}; + +#endif + +#endif diff --git a/src/fs/Limits.hxx b/src/fs/Limits.hxx index 432897a69..b574a9c9a 100644 --- a/src/fs/Limits.hxx +++ b/src/fs/Limits.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/NarrowPath.hxx b/src/fs/NarrowPath.hxx new file mode 100644 index 000000000..433a9c1cd --- /dev/null +++ b/src/fs/NarrowPath.hxx @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2003-2015 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_FS_NARROW_PATH_HXX +#define MPD_FS_NARROW_PATH_HXX + +#include "check.h" +#include "Path.hxx" +#include "util/Macros.hxx" + +#ifdef _UNICODE +#include "lib/icu/Win32.hxx" +#include "util/AllocatedString.hxx" +#include <windows.h> +#else +#include "util/StringPointer.hxx" +#endif + +/** + * A path name that uses the regular (narrow) "char". This is used to + * pass a #Path (which may be represented by wchar_t) to a library + * that accepts only "const char *". + */ +class NarrowPath { +#ifdef _UNICODE + typedef AllocatedString<> Value; +#else + typedef StringPointer<> Value; +#endif + typedef typename Value::const_pointer const_pointer; + + Value value; + +public: +#ifdef _UNICODE + explicit NarrowPath(Path _path) + :value(WideCharToMultiByte(CP_ACP, _path.c_str())) { + if (value.IsNull()) + /* fall back to empty string */ + value = Value::Empty(); + } +#else + explicit NarrowPath(Path _path):value(_path.c_str()) {} +#endif + + operator const_pointer() const { + return c_str(); + } + + const_pointer c_str() const { + return value.c_str(); + } +}; + +#endif diff --git a/src/fs/Path.cxx b/src/fs/Path.cxx index 8288a4fec..99a4ffb61 100644 --- a/src/fs/Path.cxx +++ b/src/fs/Path.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,9 +20,22 @@ #include "config.h" #include "Path.hxx" #include "Charset.hxx" +#include "util/UriUtil.hxx" +#include "util/StringUtil.hxx" std::string Path::ToUTF8() const { return ::PathToUTF8(c_str()); } + +Path::const_pointer +Path::GetSuffix() const +{ + const auto base = GetBase().c_str(); + const auto *dot = StringFindLast(base, '.'); + if (dot == nullptr || dot == base) + return nullptr; + + return dot + 1; +} diff --git a/src/fs/Path.hxx b/src/fs/Path.hxx index 9e0fa5aeb..43818b2da 100644 --- a/src/fs/Path.hxx +++ b/src/fs/Path.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,8 @@ #include <string> #include <assert.h> -#include <string.h> + +class AllocatedPath; /** * A path name in the native file system character set. @@ -35,14 +36,10 @@ * This class manages a pointer to an existing path string. While an * instance lives, the string must not be invalidated. */ -class Path { - typedef PathTraitsFS::value_type value_type; - typedef PathTraitsFS::pointer pointer; - typedef PathTraitsFS::const_pointer const_pointer; - - const char *value; +class Path : public PathTraitsFS::Pointer { + typedef PathTraitsFS::Pointer Base; - constexpr Path(const_pointer _value):value(_value) {} + constexpr Path(const_pointer _value):Base(_value) {} public: /** @@ -78,7 +75,7 @@ public: * must not be used. */ bool IsNull() const { - return value == nullptr; + return Base::IsNull(); } /** @@ -87,7 +84,7 @@ public: * @see IsNull() */ void SetNull() { - value = nullptr; + *this = nullptr; } /** @@ -96,9 +93,9 @@ public: */ gcc_pure size_t length() const { - assert(value != nullptr); + assert(!IsNull()); - return strlen(value); + return PathTraitsFS::GetLength(c_str()); } /** @@ -108,7 +105,7 @@ public: */ gcc_pure const_pointer c_str() const { - return value; + return Base::c_str(); } /** @@ -117,7 +114,17 @@ public: */ gcc_pure const_pointer data() const { - return value; + return c_str(); + } + + /** + * Does the path contain a newline character? (Which is + * usually rejected by MPD because its protocol cannot + * transfer newline characters). + */ + gcc_pure + bool HasNewline() const { + return PathTraitsFS::Find(c_str(), '\n') != nullptr; } /** @@ -129,20 +136,39 @@ public: std::string ToUTF8() const; /** + * Determine the "base" file name. + * The return value points inside this object. + */ + gcc_pure + Path GetBase() const { + return FromFS(PathTraitsFS::GetBase(c_str())); + } + + /** + * Gets directory name of this path. + * Returns a "nulled" instance on error. + */ + gcc_pure + AllocatedPath GetDirectoryName() const; + + /** * Determine the relative part of the given path to this * object, not including the directory separator. Returns an * empty string if the given path equals this object or * nullptr on mismatch. */ gcc_pure - const char *RelativeFS(const char *other_fs) const { - return PathTraitsFS::Relative(value, other_fs); + const_pointer Relative(Path other_fs) const { + return PathTraitsFS::Relative(c_str(), other_fs.c_str()); } gcc_pure - bool IsAbsolute() { + bool IsAbsolute() const { return PathTraitsFS::IsAbsolute(c_str()); } + + gcc_pure + const_pointer GetSuffix() const; }; #endif diff --git a/src/fs/Path2.cxx b/src/fs/Path2.cxx new file mode 100644 index 000000000..b85909f79 --- /dev/null +++ b/src/fs/Path2.cxx @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2003-2015 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 "Path.hxx" +#include "AllocatedPath.hxx" + +AllocatedPath +Path::GetDirectoryName() const +{ + return AllocatedPath::FromFS(PathTraitsFS::GetParent(c_str())); +} diff --git a/src/fs/StandardDirectory.cxx b/src/fs/StandardDirectory.cxx index 7a836f906..38ea804a0 100644 --- a/src/fs/StandardDirectory.cxx +++ b/src/fs/StandardDirectory.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -112,7 +112,7 @@ static inline AllocatedPath SafePathFromFS(PathTraitsFS::const_pointer dir) #ifdef WIN32 static AllocatedPath GetStandardDir(int folder_id) { - std::array<char, MAX_PATH> dir; + std::array<PathTraitsFS::value_type, MAX_PATH> dir; auto ret = SHGetFolderPath(nullptr, folder_id | CSIDL_FLAG_DONT_VERIFY, nullptr, SHGFP_TYPE_CURRENT, dir.data()); if (FAILED(ret)) @@ -287,7 +287,7 @@ AllocatedPath GetSystemConfigDir() AllocatedPath GetAppBaseDir() { - std::array<char, MAX_PATH> app; + std::array<PathTraitsFS::value_type, MAX_PATH> app; auto ret = GetModuleFileName(nullptr, app.data(), app.size()); // Check for error diff --git a/src/fs/StandardDirectory.hxx b/src/fs/StandardDirectory.hxx index e3fba375a..d453d109d 100644 --- a/src/fs/StandardDirectory.hxx +++ b/src/fs/StandardDirectory.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/Traits.cxx b/src/fs/Traits.cxx index 166b31f4e..7eba0916a 100644 --- a/src/fs/Traits.cxx +++ b/src/fs/Traits.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "Traits.hxx" +#include "util/StringUtil.hxx" #include <string.h> @@ -74,7 +75,7 @@ GetParentPathImpl(typename Traits::const_pointer p) typename Traits::const_pointer sep = Traits::FindLastSeparator(p); if (sep == nullptr) - return typename Traits::string("."); + return typename Traits::string(Traits::CURRENT_DIRECTORY); if (sep == p) return typename Traits::string(p, p + 1); #ifdef WIN32 @@ -92,13 +93,12 @@ RelativePathImpl(typename Traits::const_pointer base, assert(base != nullptr); assert(other != nullptr); - const auto base_length = Traits::GetLength(base); - if (memcmp(base, other, base_length * sizeof(*base)) != 0) + other = StringAfterPrefix(other, base); + if (other == nullptr) /* mismatch */ return nullptr; - other += base_length; - if (other != 0) { + if (*other != 0) { if (!Traits::IsSeparator(*other)) /* mismatch */ return nullptr; diff --git a/src/fs/Traits.hxx b/src/fs/Traits.hxx index 1af8f8672..08d798f05 100644 --- a/src/fs/Traits.hxx +++ b/src/fs/Traits.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,24 +22,38 @@ #include "check.h" #include "Compiler.h" +#include "util/StringPointer.hxx" +#include "util/StringAPI.hxx" #ifdef WIN32 #include "util/CharUtil.hxx" +#include <tchar.h> #endif #include <string> -#include <string.h> #include <assert.h> +#ifdef WIN32 +#define PATH_LITERAL(s) _T(s) +#else +#define PATH_LITERAL(s) (s) +#endif + /** * This class describes the nature of a native filesystem path. */ struct PathTraitsFS { +#ifdef WIN32 + typedef std::wstring string; +#else typedef std::string string; - typedef char value_type; - typedef value_type *pointer; - typedef const value_type *const_pointer; +#endif + typedef string::traits_type char_traits; + typedef char_traits::char_type value_type; + typedef StringPointer<value_type> Pointer; + typedef Pointer::pointer pointer; + typedef Pointer::const_pointer const_pointer; #ifdef WIN32 static constexpr value_type SEPARATOR = '\\'; @@ -47,6 +61,8 @@ struct PathTraitsFS { static constexpr value_type SEPARATOR = '/'; #endif + static constexpr const_pointer CURRENT_DIRECTORY = PATH_LITERAL("."); + static constexpr bool IsSeparator(value_type ch) { return #ifdef WIN32 @@ -68,7 +84,7 @@ struct PathTraitsFS { --pos; return IsSeparator(*pos) ? pos : nullptr; #else - return strrchr(p, SEPARATOR); + return StringFindLast(p, SEPARATOR); #endif } @@ -95,7 +111,12 @@ struct PathTraitsFS { gcc_pure gcc_nonnull_all static size_t GetLength(const_pointer p) { - return strlen(p); + return StringLength(p); + } + + gcc_pure gcc_nonnull_all + static const_pointer Find(const_pointer p, value_type ch) { + return StringFind(p, ch); } /** @@ -143,12 +164,15 @@ struct PathTraitsFS { */ struct PathTraitsUTF8 { typedef std::string string; - typedef char value_type; + typedef string::traits_type char_traits; + typedef char_traits::char_type value_type; typedef value_type *pointer; typedef const value_type *const_pointer; static constexpr value_type SEPARATOR = '/'; + static constexpr const_pointer CURRENT_DIRECTORY = "."; + static constexpr bool IsSeparator(value_type ch) { return ch == SEPARATOR; } @@ -186,7 +210,12 @@ struct PathTraitsUTF8 { gcc_pure gcc_nonnull_all static size_t GetLength(const_pointer p) { - return strlen(p); + return StringLength(p); + } + + gcc_pure gcc_nonnull_all + static const_pointer Find(const_pointer p, value_type ch) { + return StringFind(p, ch); } /** diff --git a/src/fs/io/AutoGunzipReader.cxx b/src/fs/io/AutoGunzipReader.cxx index 2552f7b99..b6d30dfd7 100644 --- a/src/fs/io/AutoGunzipReader.cxx +++ b/src/fs/io/AutoGunzipReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/AutoGunzipReader.hxx b/src/fs/io/AutoGunzipReader.hxx index 9f031e0f5..29a794aed 100644 --- a/src/fs/io/AutoGunzipReader.hxx +++ b/src/fs/io/AutoGunzipReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/BufferedOutputStream.cxx b/src/fs/io/BufferedOutputStream.cxx index 088a3e279..2268eb50c 100644 --- a/src/fs/io/BufferedOutputStream.cxx +++ b/src/fs/io/BufferedOutputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,17 +41,22 @@ bool BufferedOutputStream::Write(const void *data, size_t size) { if (gcc_unlikely(last_error.IsDefined())) + /* the stream has already failed */ return false; + /* try to append to the current buffer */ if (AppendToBuffer(data, size)) return true; + /* not enough room in the buffer - flush it */ if (!Flush()) return false; + /* see if there's now enough room */ if (AppendToBuffer(data, size)) return true; + /* too large for the buffer: direct write */ return os.Write(data, size, last_error); } diff --git a/src/fs/io/BufferedOutputStream.hxx b/src/fs/io/BufferedOutputStream.hxx index f2de758a2..63a3f4aee 100644 --- a/src/fs/io/BufferedOutputStream.hxx +++ b/src/fs/io/BufferedOutputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -30,6 +30,14 @@ class OutputStream; class Error; +/** + * An #OutputStream wrapper that buffers its output to reduce the + * number of OutputStream::Write() calls. + * + * It simplifies error handling by managing an #Error attribute. + * Invoke any number of writes, and check for errors in the end using + * Check(). + */ class BufferedOutputStream { OutputStream &os; @@ -47,11 +55,18 @@ public: gcc_printf(2,3) bool Format(const char *fmt, ...); + /** + * Returns false if an error has occurred. + */ gcc_pure bool Check() const { return !last_error.IsDefined(); } + /** + * Returns false if an error has occurred. In that case, a + * copy of the #Error is returned. + */ bool Check(Error &error) const { if (last_error.IsDefined()) { error.Set(last_error); @@ -60,6 +75,9 @@ public: return true; } + /** + * Write buffer contents to the #OutputStream. + */ bool Flush(); bool Flush(Error &error); diff --git a/src/fs/io/BufferedReader.cxx b/src/fs/io/BufferedReader.cxx index ba2f17dcf..9a296d815 100644 --- a/src/fs/io/BufferedReader.cxx +++ b/src/fs/io/BufferedReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -59,8 +59,10 @@ BufferedReader::ReadLine() { do { char *line = ReadBufferedLine(buffer); - if (line != nullptr) + if (line != nullptr) { + ++line_number; return line; + } } while (Fill(true)); if (last_error.IsDefined() || !eof || buffer.IsEmpty()) @@ -78,5 +80,6 @@ BufferedReader::ReadLine() char *line = buffer.Read().data; buffer.Clear(); + ++line_number; return line; } diff --git a/src/fs/io/BufferedReader.hxx b/src/fs/io/BufferedReader.hxx index 61cc8df83..a0c42d23c 100644 --- a/src/fs/io/BufferedReader.hxx +++ b/src/fs/io/BufferedReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,9 +41,12 @@ class BufferedReader { bool eof; + unsigned line_number; + public: BufferedReader(Reader &_reader) - :reader(_reader), buffer(4096), eof(false) {} + :reader(_reader), buffer(4096), eof(false), + line_number(0) {} gcc_pure bool Check() const { @@ -70,6 +73,10 @@ public: } char *ReadLine(); + + unsigned GetLineNumber() const { + return line_number; + } }; #endif diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx index 0eff8b5f0..a4ef8f6b4 100644 --- a/src/fs/io/FileOutputStream.cxx +++ b/src/fs/io/FileOutputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,36 +20,61 @@ #include "config.h" #include "FileOutputStream.hxx" #include "fs/FileSystem.hxx" -#include "system/fd_util.h" #include "util/Error.hxx" +FileOutputStream * +FileOutputStream::Create(Path path, Error &error) +{ + FileOutputStream *f = new FileOutputStream(path, error); + if (!f->IsDefined()) { + delete f; + f = nullptr; + } + + return f; +} + #ifdef WIN32 FileOutputStream::FileOutputStream(Path _path, Error &error) - :path(_path), - handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr, - TRUNCATE_EXISTING, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, - nullptr)) + :BaseFileOutputStream(_path) { - if (handle == INVALID_HANDLE_VALUE) - error.FormatLastError("Failed to create %s", path.c_str()); + SetHandle(CreateFile(_path.c_str(), GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, + nullptr)); + if (!IsDefined()) + error.FormatLastError("Failed to create %s", + GetPath().ToUTF8().c_str()); +} + +uint64_t +BaseFileOutputStream::Tell() const +{ + LONG high = 0; + DWORD low = SetFilePointer(handle, 0, &high, FILE_CURRENT); + if (low == 0xffffffff) + return 0; + + return uint64_t(high) << 32 | uint64_t(low); } bool -FileOutputStream::Write(const void *data, size_t size, Error &error) +BaseFileOutputStream::Write(const void *data, size_t size, Error &error) { assert(IsDefined()); DWORD nbytes; if (!WriteFile(handle, data, size, &nbytes, nullptr)) { - error.FormatLastError("Failed to write to %s", path.c_str()); + error.FormatLastError("Failed to write to %s", + path.ToUTF8().c_str()); return false; } if (size_t(nbytes) != size) { error.FormatLastError(ERROR_DISK_FULL, - "Failed to write to %s", path.c_str()); + "Failed to write to %s", + path.ToUTF8().c_str()); return false; } @@ -61,8 +86,7 @@ FileOutputStream::Commit(gcc_unused Error &error) { assert(IsDefined()); - CloseHandle(handle); - handle = INVALID_HANDLE_VALUE; + Close(); return true; } @@ -71,9 +95,8 @@ FileOutputStream::Cancel() { assert(IsDefined()); - CloseHandle(handle); - handle = INVALID_HANDLE_VALUE; - RemoveFile(path); + Close(); + RemoveFile(GetPath()); } #else @@ -82,28 +105,66 @@ FileOutputStream::Cancel() #include <unistd.h> #include <errno.h> +#ifdef HAVE_LINKAT +#ifndef O_TMPFILE +/* supported since Linux 3.11 */ +#define __O_TMPFILE 020000000 +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +#include <stdio.h> +#endif + +/** + * Open a file using Linux's O_TMPFILE for writing the given file. + */ +static bool +OpenTempFile(FileDescriptor &fd, Path path) +{ + const auto directory = path.GetDirectoryName(); + if (directory.IsNull()) + return false; + + return fd.Open(directory.c_str(), O_TMPFILE|O_WRONLY, 0666); +} + +#endif /* HAVE_LINKAT */ + FileOutputStream::FileOutputStream(Path _path, Error &error) - :path(_path), - fd(open_cloexec(path.c_str(), - O_WRONLY|O_CREAT|O_TRUNC, - 0666)) + :BaseFileOutputStream(_path) +{ +#ifdef HAVE_LINKAT + /* try Linux's O_TMPFILE first */ + is_tmpfile = OpenTempFile(SetFD(), GetPath()); + if (!is_tmpfile) { +#endif + /* fall back to plain POSIX */ + if (!SetFD().Open(GetPath().c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + 0666)) + error.FormatErrno("Failed to create %s", + GetPath().c_str()); +#ifdef HAVE_LINKAT + } +#endif +} + +uint64_t +BaseFileOutputStream::Tell() const { - if (fd < 0) - error.FormatErrno("Failed to create %s", path.c_str()); + return fd.Tell(); } bool -FileOutputStream::Write(const void *data, size_t size, Error &error) +BaseFileOutputStream::Write(const void *data, size_t size, Error &error) { assert(IsDefined()); - ssize_t nbytes = write(fd, data, size); + ssize_t nbytes = fd.Write(data, size); if (nbytes < 0) { - error.FormatErrno("Failed to write to %s", path.c_str()); + error.FormatErrno("Failed to write to %s", GetPath().c_str()); return false; } else if ((size_t)nbytes < size) { error.FormatErrno(ENOSPC, - "Failed to write to %s", path.c_str()); + "Failed to write to %s", GetPath().c_str()); return false; } @@ -115,10 +176,27 @@ FileOutputStream::Commit(Error &error) { assert(IsDefined()); - bool success = close(fd) == 0; - fd = -1; +#if HAVE_LINKAT + if (is_tmpfile) { + RemoveFile(GetPath()); + + /* hard-link the temporary file to the final path */ + char fd_path[64]; + snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", + GetFD().Get()); + if (linkat(AT_FDCWD, fd_path, AT_FDCWD, GetPath().c_str(), + AT_SYMLINK_FOLLOW) < 0) { + error.FormatErrno("Failed to commit %s", + GetPath().c_str()); + Close(); + return false; + } + } +#endif + + bool success = Close(); if (!success) - error.FormatErrno("Failed to commit %s", path.c_str()); + error.FormatErrno("Failed to commit %s", GetPath().c_str()); return success; } @@ -128,10 +206,53 @@ FileOutputStream::Cancel() { assert(IsDefined()); - close(fd); - fd = -1; + Close(); + +#ifdef HAVE_LINKAT + if (!is_tmpfile) +#endif + RemoveFile(GetPath()); +} + +#endif + +AppendFileOutputStream::AppendFileOutputStream(Path _path, Error &error) + :BaseFileOutputStream(_path) +{ +#ifdef WIN32 + SetHandle(CreateFile(GetPath().c_str(), GENERIC_WRITE, 0, nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, + nullptr)); + if (!IsDefined()) + error.FormatLastError("Failed to append to %s", + GetPath().ToUTF8().c_str()); - RemoveFile(path); + if (!SeekEOF()) { + error.FormatLastError("Failed seek end-of-file of %s", + GetPath().ToUTF8().c_str()); + Close(); + } +#else + if (!SetFD().Open(GetPath().c_str(), + O_WRONLY|O_APPEND)) + error.FormatErrno("Failed to append to %s", + GetPath().c_str()); +#endif } +bool +AppendFileOutputStream::Commit(gcc_unused Error &error) +{ + assert(IsDefined()); + +#ifdef WIN32 + return Close(); +#else + bool success = Close(); + if (!success) + error.FormatErrno("Failed to commit %s", GetPath().c_str()); + + return success; #endif +} diff --git a/src/fs/io/FileOutputStream.hxx b/src/fs/io/FileOutputStream.hxx index 5b6309957..b182fd03f 100644 --- a/src/fs/io/FileOutputStream.hxx +++ b/src/fs/io/FileOutputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,10 @@ #include "fs/AllocatedPath.hxx" #include "Compiler.h" +#ifndef WIN32 +#include "system/FileDescriptor.hxx" +#endif + #include <assert.h> #ifdef WIN32 @@ -33,37 +37,124 @@ class Path; -class FileOutputStream final : public OutputStream { - AllocatedPath path; +class BaseFileOutputStream : public OutputStream { + const AllocatedPath path; #ifdef WIN32 HANDLE handle; #else - int fd; + FileDescriptor fd; #endif -public: - FileOutputStream(Path _path, Error &error); +protected: +#ifdef WIN32 + template<typename P> + BaseFileOutputStream(P &&_path) + :path(std::forward<P>(_path)), + handle(INVALID_HANDLE_VALUE) {} +#else + template<typename P> + BaseFileOutputStream(P &&_path) + :path(std::forward<P>(_path)), + fd(FileDescriptor::Undefined()) {} +#endif - ~FileOutputStream() { - if (IsDefined()) - Cancel(); + ~BaseFileOutputStream() { + assert(!IsDefined()); } +#ifdef WIN32 + void SetHandle(HANDLE _handle) { + assert(!IsDefined()); + + handle = _handle; + + assert(IsDefined()); + } +#else + FileDescriptor &SetFD() { + assert(!IsDefined()); + + return fd; + } + + const FileDescriptor &GetFD() const { + return fd; + } +#endif + + bool Close() { + assert(IsDefined()); + +#ifdef WIN32 + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + return true; +#else + return fd.Close(); +#endif + } + +#ifdef WIN32 + bool SeekEOF() { + return SetFilePointer(handle, 0, nullptr, + FILE_END) != 0xffffffff; + } +#endif +public: bool IsDefined() const { #ifdef WIN32 return handle != INVALID_HANDLE_VALUE; #else - return fd >= 0; + return fd.IsDefined(); #endif } - bool Commit(Error &error); - void Cancel(); + Path GetPath() const { + return path; + } + + gcc_pure + uint64_t Tell() const; /* virtual methods from class OutputStream */ bool Write(const void *data, size_t size, Error &error) override; }; +class FileOutputStream final : public BaseFileOutputStream { +#ifdef HAVE_LINKAT + /** + * Was O_TMPFILE used? If yes, then linkat() must be used to + * create a link to this file. + */ + bool is_tmpfile; +#endif + +public: + FileOutputStream(Path _path, Error &error); + + ~FileOutputStream() { + if (IsDefined()) + Cancel(); + } + + static FileOutputStream *Create(Path path, Error &error); + + bool Commit(Error &error); + void Cancel(); +}; + +class AppendFileOutputStream final : public BaseFileOutputStream { +public: + AppendFileOutputStream(Path _path, Error &error); + + ~AppendFileOutputStream() { + if (IsDefined()) + Close(); + } + + bool Commit(Error &error); +}; + #endif diff --git a/src/fs/io/FileReader.cxx b/src/fs/io/FileReader.cxx index d63cd8ab0..e54f6f3a8 100644 --- a/src/fs/io/FileReader.cxx +++ b/src/fs/io/FileReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "FileReader.hxx" -#include "system/fd_util.h" +#include "fs/FileInfo.hxx" #include "util/Error.hxx" #ifdef WIN32 @@ -30,8 +30,18 @@ FileReader::FileReader(Path _path, Error &error) nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr)) { - if (handle == INVALID_HANDLE_VALUE) - error.FormatLastError("Failed to open %s", path.c_str()); + if (handle == INVALID_HANDLE_VALUE) { + const auto path_utf8 = path.ToUTF8(); + error.FormatLastError("Failed to open %s", path_utf8.c_str()); + } +} + +bool +FileReader::GetFileInfo(FileInfo &info, Error &error) const +{ + assert(IsDefined()); + + return ::GetFileInfo(path, info, error); } size_t @@ -41,13 +51,28 @@ FileReader::Read(void *data, size_t size, Error &error) DWORD nbytes; if (!ReadFile(handle, data, size, &nbytes, nullptr)) { - error.FormatLastError("Failed to read from %s", path.c_str()); + const auto path_utf8 = path.ToUTF8(); + error.FormatLastError("Failed to read from %s", + path_utf8.c_str()); nbytes = 0; } return nbytes; } +bool +FileReader::Seek(off_t offset, Error &error) +{ + assert(IsDefined()); + + auto result = SetFilePointer(handle, offset, nullptr, FILE_BEGIN); + const bool success = result != INVALID_SET_FILE_POINTER; + if (!success) + error.SetLastError("Failed to seek"); + + return success; +} + void FileReader::Close() { @@ -58,26 +83,33 @@ FileReader::Close() #else -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> - FileReader::FileReader(Path _path, Error &error) - :path(_path), - fd(open_cloexec(path.c_str(), - O_RDONLY, - 0)) + :path(_path) { - if (fd < 0) + fd.OpenReadOnly(path.c_str()); + if (!fd.IsDefined()) error.FormatErrno("Failed to open %s", path.c_str()); } +bool +FileReader::GetFileInfo(FileInfo &info, Error &error) const +{ + assert(IsDefined()); + + const bool success = fstat(fd.Get(), &info.st) == 0; + if (!success) + error.FormatErrno("Failed to access %s", + path.ToUTF8().c_str()); + + return success; +} + size_t FileReader::Read(void *data, size_t size, Error &error) { assert(IsDefined()); - ssize_t nbytes = read(fd, data, size); + ssize_t nbytes = fd.Read(data, size); if (nbytes < 0) { error.FormatErrno("Failed to read from %s", path.c_str()); nbytes = 0; @@ -86,13 +118,25 @@ FileReader::Read(void *data, size_t size, Error &error) return nbytes; } +bool +FileReader::Seek(off_t offset, Error &error) +{ + assert(IsDefined()); + + auto result = fd.Seek(offset); + const bool success = result >= 0; + if (!success) + error.SetErrno("Failed to seek"); + + return success; +} + void FileReader::Close() { assert(IsDefined()); - close(fd); - fd = -1; + fd.Close(); } #endif diff --git a/src/fs/io/FileReader.hxx b/src/fs/io/FileReader.hxx index 9f459aee2..642fc5ed1 100644 --- a/src/fs/io/FileReader.hxx +++ b/src/fs/io/FileReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,10 @@ #include "fs/AllocatedPath.hxx" #include "Compiler.h" +#ifndef WIN32 +#include "system/FileDescriptor.hxx" +#endif + #include <assert.h> #ifdef WIN32 @@ -32,6 +36,7 @@ #endif class Path; +class FileInfo; class FileReader final : public Reader { AllocatedPath path; @@ -39,12 +44,26 @@ class FileReader final : public Reader { #ifdef WIN32 HANDLE handle; #else - int fd; + FileDescriptor fd; #endif public: FileReader(Path _path, Error &error); +#ifdef WIN32 + FileReader(FileReader &&other) + :path(std::move(other.path)), + handle(other.handle) { + other.handle = INVALID_HANDLE_VALUE; + } +#else + FileReader(FileReader &&other) + :path(std::move(other.path)), + fd(other.fd) { + other.fd.SetUndefined(); + } +#endif + ~FileReader() { if (IsDefined()) Close(); @@ -55,12 +74,22 @@ public: #ifdef WIN32 return handle != INVALID_HANDLE_VALUE; #else - return fd >= 0; + return fd.IsDefined(); #endif } +#ifndef WIN32 + FileDescriptor GetFD() const { + return fd; + } +#endif + void Close(); + bool GetFileInfo(FileInfo &info, Error &error) const; + + bool Seek(off_t offset, Error &error); + /* virtual methods from class Reader */ size_t Read(void *data, size_t size, Error &error) override; }; diff --git a/src/fs/io/GunzipReader.cxx b/src/fs/io/GunzipReader.cxx index ad5e41784..78f5b2c69 100644 --- a/src/fs/io/GunzipReader.cxx +++ b/src/fs/io/GunzipReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/GunzipReader.hxx b/src/fs/io/GunzipReader.hxx index 06c44bad6..381d1af5e 100644 --- a/src/fs/io/GunzipReader.hxx +++ b/src/fs/io/GunzipReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/GzipOutputStream.cxx b/src/fs/io/GzipOutputStream.cxx index 27ae6b2ad..d2a693b87 100644 --- a/src/fs/io/GzipOutputStream.cxx +++ b/src/fs/io/GzipOutputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/GzipOutputStream.hxx b/src/fs/io/GzipOutputStream.hxx index 27ee2dd24..fdab7bca4 100644 --- a/src/fs/io/GzipOutputStream.hxx +++ b/src/fs/io/GzipOutputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/OutputStream.hxx b/src/fs/io/OutputStream.hxx index 71311c71f..f7d101180 100644 --- a/src/fs/io/OutputStream.hxx +++ b/src/fs/io/OutputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/PeekReader.cxx b/src/fs/io/PeekReader.cxx index 2e8042ab6..ec9520a37 100644 --- a/src/fs/io/PeekReader.cxx +++ b/src/fs/io/PeekReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/PeekReader.hxx b/src/fs/io/PeekReader.hxx index 32180b0a8..c00ed66be 100644 --- a/src/fs/io/PeekReader.hxx +++ b/src/fs/io/PeekReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/Reader.hxx b/src/fs/io/Reader.hxx index d41e92dd0..657f96ac2 100644 --- a/src/fs/io/Reader.hxx +++ b/src/fs/io/Reader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/StdioOutputStream.hxx b/src/fs/io/StdioOutputStream.hxx index c1c0a00bd..88dbe6f00 100644 --- a/src/fs/io/StdioOutputStream.hxx +++ b/src/fs/io/StdioOutputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/fs/io/TextFile.cxx b/src/fs/io/TextFile.cxx index 28d6dabcb..9866da08a 100644 --- a/src/fs/io/TextFile.cxx +++ b/src/fs/io/TextFile.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,14 +28,14 @@ TextFile::TextFile(Path path_fs, Error &error) :file_reader(new FileReader(path_fs, error)), -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB gunzip_reader(file_reader->IsDefined() ? new AutoGunzipReader(*file_reader) : nullptr), #endif buffered_reader(file_reader->IsDefined() ? new BufferedReader(* -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB gunzip_reader #else file_reader @@ -48,7 +48,7 @@ TextFile::TextFile(Path path_fs, Error &error) TextFile::~TextFile() { delete buffered_reader; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB delete gunzip_reader; #endif delete file_reader; diff --git a/src/fs/io/TextFile.hxx b/src/fs/io/TextFile.hxx index 5577363e7..bee9e8c23 100644 --- a/src/fs/io/TextFile.hxx +++ b/src/fs/io/TextFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ class BufferedReader; class TextFile { FileReader *const file_reader; -#ifdef HAVE_ZLIB +#ifdef ENABLE_ZLIB AutoGunzipReader *const gunzip_reader; #endif @@ -59,7 +59,6 @@ public: * Use Check() after nullptr has been returned to check * whether an error occurred or end-of-file has been reached. * - * @param file the source file, opened in text mode * @return a pointer to the line, or nullptr on end-of-file or error */ char *ReadLine(); diff --git a/src/haiku/App_MusicPD b/src/haiku/App_MusicPD Binary files differnew file mode 100644 index 000000000..4d341fdbf --- /dev/null +++ b/src/haiku/App_MusicPD diff --git a/src/haiku/mpd.rdef b/src/haiku/mpd.rdef new file mode 100644 index 000000000..6f1aca2d9 --- /dev/null +++ b/src/haiku/mpd.rdef @@ -0,0 +1,69 @@ +resource app_signature "application/x-vnd.MusicPD"; + +resource app_flags B_BACKGROUND_APP; + +// TODO: resource app_version {}; + +resource vector_icon { + $"6E6369661F050102031604BEE29BBEC5403EC540BEE29B4A10004A10000001C6" + $"70D073FFFF020116033EB0000000000000003EB000482000482000BE4B0084FF" + $"32020316023CAF103CF749BCF7493CAF104A07A44A1DB7A143FFFF02031602BC" + $"AB8FBCFA713CFA71BCAB8F4A05E34A004BAB08FFFF03010000020016023CC7EE" + $"389BC0BA16573E39B04977C842ADC700FFFFD3020006023C529D3753A2B8966F" + $"3D9D084B6044496AAF00474747FFA5A0A002001602BC4E76BC411B3C90DABCA0" + $"0D47587D4ABA850090FFD40200160238313C3B5CF0BFCD963C7AAC4C13943FCA" + $"F901ECFFC3054B04017E020106023E1C1538010FB7C32B3DF5E649B8BE48DD4A" + $"000593DCFF00337F020006023879063B8224BE2CC83B10DB4A1F6F49B894FF9A" + $"9A9A00242222020006033C69A60000000000003E186148800049800058F3F3F3" + $"00D4CECEFFD9D9D9038DFF06050002001602B2E4F7386B91BA78F7B4F4FD49FB" + $"A94AE12500CEFF6603010000020006023C55B638309FBA16573E39B049E9FF43" + $"840A008B8787FF161515020016023C57B5364381B785863DA4F54B27C349B7BB" + $"0010FF4C02001602BC4E76BC411B3C90DABCA00D47587D4ABA850060FF500200" + $"160238313C3B5CF0BFCD963C7AAC4C13943FCAF90174FF22055C04017E020016" + $"023879063B8224BE2CC83B10DB4A1F6F49B894FF59001E020016033C3DA60000" + $"000000003E186148AC0049800058AD0076FA800333C805020106023A40000000" + $"000000003980004A300048400000767676FC403E3E020106023E1C1538010FB7" + $"C32B3DF5E649B8BE48DD4A000593DCFF00337F05002102044022C65922B92622" + $"224022B92622C659405EB9265EC6595E5E405EC6595EB9260A062E262E4B4C5A" + $"5650562C38220A064C5C545C604FCA1BC4875C4A58480A042E264C32562C3822" + $"0A044C324C5A5650562C0A042E262E4B4C5A4C320A044934494E3043302A0A04" + $"BA29C0283043302ABA31B7540A04494B494E304332C0270A044934494B3241BA" + $"31B7540A043E25432252264D2A08043E2543225226522C0A034D2A5226522C08" + $"02464F47C5ED0A043246324A4453444E0A04344834494250424E08025436503A" + $"0A06262E264C485E5252523430280A04262E483C523430280A04483C485E5252" + $"52340A04262E264C485E483C0A04443D4456284928320A04B6F9C28C28492832" + $"B701BA840A044454445728492AC28B0A04443D44542A47B701BA840A04382B3D" + $"284E2D49310804382B3D284E2D4E330A0349304E2C4E320802425843C9830A06" + $"486054606052CA1BC5B95C4D524802044530C2D730C0A430403540BAB540BC06" + $"4538C0A438C2D7384A354ABC064ABAB50803452145335B250A044934494B3241" + $"BA31B754240A0B0102023EF45B0000000000003EF45B487749B685270A05010B" + $"1A3EF45B0000000000003EF45B487749B6852715FF01178400040A09010A0A3E" + $"F45B0000000000003EF45B487749B6852715FF0A0A010C023EF45B0000000000" + $"003EF45B487749B685270A050101123EF45B0000000000003EF45B487749B685" + $"2701178400040A060103023EF45B0000000000003EF45B487749B685270A0701" + $"04023EF45B0000000000003EF45B487749B685270A08020506023EF45B000000" + $"0000003EF45B487749B685270A0C0109023EF45B0000000000003EF45B487749" + $"B685270A0D01070A3EF45B0000000000003EF45B487749B6852715FF0A0E0108" + $"0A3EF45B0000000000003EF45B487749B6852715FF0A0F010D1A3EF45B000000" + $"0000003EF45B487749B6852715FF01178220040A0A010E0A3EF45B0000000000" + $"003EF45B487749B6852715FF0A11010F0A3EF45B0000000000003EF45B487749" + $"B6852715FF0A0B01000A3D43F93C2B26BD304E3DF9DE48FCA544AB2E15FF0A00" + $"0100123CF8FE3C832FBCE7163E4DA3480D86C5C7B501178400040A010100023C" + $"F8FE3C832FBCE7163E4DA3480D86C5C7B50A020100023AC6433A584EBAB58C3C" + $"265448124D45D0400A0301001A3CB1A73C46E3BCA16C3E165C480E5DC4720015" + $"FF01178200040A0401001A3C808E3C1D64BC71B33DE0FC480EF2C30DBC15FF01" + $"178200040A18011D023EAAAA0000000000003EAAAA47155548B0480A12011A1A" + $"3EAAAA0000000000003EAAAA47155548B04815FF01178400040A1601190A3EAA" + $"AA0000000000003EAAAA47155548B04815FF0A17011B0A3EAAAA000000000000" + $"3EAAAA47155548B04815FF0A120111123EAAAA0000000000003EAAAA47155548" + $"B04801178400040A130112023EAAAA0000000000003EAAAA47155548B0480A14" + $"0113023EAAAA0000000000003EAAAA47155548B0480A15021415023EAAAA0000" + $"000000003EAAAA47155548B0480A1D01180A3EAAAA0000000000003EAAAA4715" + $"5548B04815FF0A1D01180A3EAAAA0000000000003EAAAA47155548B04800150A" + $"1901160A3EAAAA0000000000003EAAAA47155548B04815FF0A1A01170A3EAAAA" + $"0000000000003EAAAA47155548B04815FF0A1B011C1A3EAAAA0000000000003E" + $"AAAA47155548B04815FF01178220040A1C011E023EAAAA0000000000003EAAAA" + $"47155548B0480A12011F1A3EAAAA0000000000003EAAAA47155548B04815FF01" + $"178222040A12011F1A3EAAAA0000000000003EAAAA47155548B0480015011784" + $"2204" +}; diff --git a/src/input/AsyncInputStream.cxx b/src/input/AsyncInputStream.cxx index 5795ecead..68cb8ff68 100644 --- a/src/input/AsyncInputStream.cxx +++ b/src/input/AsyncInputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/AsyncInputStream.hxx b/src/input/AsyncInputStream.hxx index d1f0c3b9d..64f566a97 100644 --- a/src/input/AsyncInputStream.hxx +++ b/src/input/AsyncInputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -62,6 +62,10 @@ protected: Error postponed_error; public: + /** + * @param _buffer a buffer allocated with HugeAllocate(); the + * destructor will free it using HugeFree() + */ AsyncInputStream(const char *_url, Mutex &_mutex, Cond &_cond, void *_buffer, size_t _buffer_size, diff --git a/src/input/Domain.cxx b/src/input/Domain.cxx index 26ae298a4..12417af34 100644 --- a/src/input/Domain.cxx +++ b/src/input/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/Domain.hxx b/src/input/Domain.hxx index 16fa5e0f1..a18a26426 100644 --- a/src/input/Domain.hxx +++ b/src/input/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/IcyInputStream.cxx b/src/input/IcyInputStream.cxx index fb82cdec6..5344a71ec 100644 --- a/src/input/IcyInputStream.cxx +++ b/src/input/IcyInputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/IcyInputStream.hxx b/src/input/IcyInputStream.hxx index d8968a741..4fca11eb3 100644 --- a/src/input/IcyInputStream.hxx +++ b/src/input/IcyInputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/Init.cxx b/src/input/Init.cxx index 0ee87c4d8..4ed44f100 100644 --- a/src/input/Init.cxx +++ b/src/input/Init.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,7 @@ #include "util/Error.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "Log.hxx" #include <assert.h> @@ -33,7 +33,7 @@ bool input_stream_global_init(Error &error) { - const config_param empty; + const ConfigBlock empty; for (unsigned i = 0; input_plugins[i] != nullptr; ++i) { const InputPlugin *plugin = input_plugins[i]; @@ -42,16 +42,17 @@ input_stream_global_init(Error &error) assert(*plugin->name != 0); assert(plugin->open != nullptr); - const struct config_param *param = - config_find_block(CONF_INPUT, "plugin", plugin->name); - if (param == nullptr) { - param = ∅ - } else if (!param->GetBlockValue("enabled", true)) + const auto *block = + config_find_block(ConfigBlockOption::INPUT, "plugin", + plugin->name); + if (block == nullptr) { + block = ∅ + } else if (!block->GetBlockValue("enabled", true)) /* the plugin is disabled in mpd.conf */ continue; InputPlugin::InitResult result = plugin->init != nullptr - ? plugin->init(*param, error) + ? plugin->init(*block, error) : InputPlugin::InitResult::SUCCESS; switch (result) { diff --git a/src/input/Init.hxx b/src/input/Init.hxx index 875fdce7c..eb4f8b1b2 100644 --- a/src/input/Init.hxx +++ b/src/input/Init.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,14 +23,15 @@ class Error; /** - * Initializes this library and all input_stream implementations. + * Initializes this library and all #InputStream implementations. */ bool input_stream_global_init(Error &error); /** - * Deinitializes this library and all input_stream implementations. + * Deinitializes this library and all #InputStream implementations. */ -void input_stream_global_finish(void); +void +input_stream_global_finish(); #endif diff --git a/src/input/InputPlugin.hxx b/src/input/InputPlugin.hxx index c2adb419c..a7c19bef3 100644 --- a/src/input/InputPlugin.hxx +++ b/src/input/InputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ #endif #endif -struct config_param; +struct ConfigBlock; class InputStream; class Error; struct Tag; @@ -69,13 +69,13 @@ struct InputPlugin { * @return true on success, false if the plugin should be * disabled */ - InitResult (*init)(const config_param ¶m, Error &error); + InitResult (*init)(const ConfigBlock &block, Error &error); /** * Global deinitialization. Called once before MPD shuts * down (only if init() has returned true). */ - void (*finish)(void); + void (*finish)(); InputStream *(*open)(const char *uri, Mutex &mutex, Cond &cond, diff --git a/src/input/InputStream.cxx b/src/input/InputStream.cxx index 44f726a62..bf6fd198e 100644 --- a/src/input/InputStream.cxx +++ b/src/input/InputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -133,9 +133,38 @@ InputStream::LockRead(void *ptr, size_t _size, Error &error) } bool +InputStream::ReadFull(void *_ptr, size_t _size, Error &error) +{ + uint8_t *ptr = (uint8_t *)_ptr; + + size_t nbytes_total = 0; + while (_size > 0) { + size_t nbytes = Read(ptr + nbytes_total, _size, error); + if (nbytes == 0) + return false; + + nbytes_total += nbytes; + _size -= nbytes; + } + return true; +} + +bool +InputStream::LockReadFull(void *ptr, size_t _size, Error &error) +{ +#if !CLANG_CHECK_VERSION(3,6) + /* disabled on clang due to -Wtautological-pointer-compare */ + assert(ptr != nullptr); +#endif + assert(_size > 0); + + const ScopeLock protect(mutex); + return ReadFull(ptr, _size, error); +} + +bool InputStream::LockIsEOF() { const ScopeLock protect(mutex); return IsEOF(); } - diff --git a/src/input/InputStream.hxx b/src/input/InputStream.hxx index 81b903ba2..bf628ea64 100644 --- a/src/input/InputStream.hxx +++ b/src/input/InputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -217,13 +217,6 @@ public: mime = std::move(_mime); } - gcc_nonnull_all - void OverrideMimeType(const char *_mime) { - assert(ready); - - mime = _mime; - } - gcc_pure bool KnownSize() const { assert(ready); @@ -350,7 +343,6 @@ public: * * 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 @@ -364,6 +356,25 @@ public: */ gcc_nonnull_all size_t LockRead(void *ptr, size_t size, Error &error); + + /** + * Reads the whole data from the stream into the caller-supplied buffer. + * + * The caller must lock the mutex. + * + * @param ptr the buffer to read into + * @param size the number of bytes to read + * @return true if the whole data was read, false otherwise. + */ + gcc_nonnull_all + bool ReadFull(void *ptr, size_t size, Error &error); + + /** + * Wrapper for ReadFull() which locks and unlocks the mutex; + * the caller must not be holding it already. + */ + gcc_nonnull_all + bool LockReadFull(void *ptr, size_t size, Error &error); }; #endif diff --git a/src/input/LocalOpen.cxx b/src/input/LocalOpen.cxx index ad8eba8ce..25644bae1 100644 --- a/src/input/LocalOpen.cxx +++ b/src/input/LocalOpen.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/LocalOpen.hxx b/src/input/LocalOpen.hxx index cf1b2b632..6f4ef2a2c 100644 --- a/src/input/LocalOpen.hxx +++ b/src/input/LocalOpen.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/Offset.hxx b/src/input/Offset.hxx index 552397904..fbda933ba 100644 --- a/src/input/Offset.hxx +++ b/src/input/Offset.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/Open.cxx b/src/input/Open.cxx index 66ccdce74..6bcca0b84 100644 --- a/src/input/Open.cxx +++ b/src/input/Open.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include "plugins/RewindInputPlugin.hxx" #include "fs/Traits.hxx" #include "fs/Path.hxx" +#include "fs/AllocatedPath.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -34,10 +35,14 @@ InputStream::Open(const char *url, Mutex &mutex, Cond &cond, Error &error) { - if (PathTraitsFS::IsAbsolute(url)) - /* TODO: the parameter is UTF-8, not filesystem charset */ - return OpenLocalInputStream(Path::FromFS(url), + if (PathTraitsUTF8::IsAbsolute(url)) { + const auto path = AllocatedPath::FromUTF8(url, error); + if (path.IsNull()) + return nullptr; + + return OpenLocalInputStream(path, mutex, cond, error); + } input_plugins_for_each_enabled(plugin) { InputStream *is; diff --git a/src/input/ProxyInputStream.cxx b/src/input/ProxyInputStream.cxx index 74a272f6a..013d880d8 100644 --- a/src/input/ProxyInputStream.cxx +++ b/src/input/ProxyInputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/ProxyInputStream.hxx b/src/input/ProxyInputStream.hxx index 727ae5917..c91c1a952 100644 --- a/src/input/ProxyInputStream.hxx +++ b/src/input/ProxyInputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/Registry.cxx b/src/input/Registry.cxx index 748c18ca8..e33e51bb9 100644 --- a/src/input/Registry.cxx +++ b/src/input/Registry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ #include "util/Macros.hxx" #include "plugins/FileInputPlugin.hxx" -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA #include "plugins/AlsaInputPlugin.hxx" #endif @@ -34,7 +34,7 @@ #include "plugins/CurlInputPlugin.hxx" #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG #include "plugins/FfmpegInputPlugin.hxx" #endif @@ -56,7 +56,7 @@ const InputPlugin *const input_plugins[] = { &input_plugin_file, -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA &input_plugin_alsa, #endif #ifdef ENABLE_ARCHIVE @@ -65,7 +65,7 @@ const InputPlugin *const input_plugins[] = { #ifdef ENABLE_CURL &input_plugin_curl, #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG &input_plugin_ffmpeg, #endif #ifdef ENABLE_SMBCLIENT diff --git a/src/input/Registry.hxx b/src/input/Registry.hxx index 1b81f8f06..af1b3be8b 100644 --- a/src/input/Registry.hxx +++ b/src/input/Registry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/TextInputStream.cxx b/src/input/TextInputStream.cxx index 5a8dcc065..897b2b472 100644 --- a/src/input/TextInputStream.cxx +++ b/src/input/TextInputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/TextInputStream.hxx b/src/input/TextInputStream.hxx index 6f39d22cf..d0b5d5f40 100644 --- a/src/input/TextInputStream.hxx +++ b/src/input/TextInputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -30,10 +30,10 @@ class TextInputStream { public: /** - * Wraps an existing #input_stream object into a #TextInputStream, + * Wraps an existing #InputStream object into a #TextInputStream, * to read its contents as text lines. * - * @param _is an open #input_stream object + * @param _is an open #InputStream object */ explicit TextInputStream(InputStream &_is) :is(_is) {} diff --git a/src/input/ThreadInputStream.cxx b/src/input/ThreadInputStream.cxx index 235ed2b01..061d5cbfe 100644 --- a/src/input/ThreadInputStream.cxx +++ b/src/input/ThreadInputStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/ThreadInputStream.hxx b/src/input/ThreadInputStream.hxx index c6ac7669c..6fc3e2e7b 100644 --- a/src/input/ThreadInputStream.hxx +++ b/src/input/ThreadInputStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/AlsaInputPlugin.cxx b/src/input/plugins/AlsaInputPlugin.cxx index f03f745c6..d2be734b5 100644 --- a/src/input/plugins/AlsaInputPlugin.cxx +++ b/src/input/plugins/AlsaInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/AlsaInputPlugin.hxx b/src/input/plugins/AlsaInputPlugin.hxx index dddf7dfd7..eb50ec8d6 100644 --- a/src/input/plugins/AlsaInputPlugin.hxx +++ b/src/input/plugins/AlsaInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/ArchiveInputPlugin.cxx b/src/input/plugins/ArchiveInputPlugin.cxx index da3d7ca71..b6472e00a 100644 --- a/src/input/plugins/ArchiveInputPlugin.cxx +++ b/src/input/plugins/ArchiveInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/ArchiveInputPlugin.hxx b/src/input/plugins/ArchiveInputPlugin.hxx index b6158684a..79331cd5a 100644 --- a/src/input/plugins/ArchiveInputPlugin.hxx +++ b/src/input/plugins/ArchiveInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/CdioParanoiaInputPlugin.cxx b/src/input/plugins/CdioParanoiaInputPlugin.cxx index f847b35c1..dda5cb83f 100644 --- a/src/input/plugins/CdioParanoiaInputPlugin.cxx +++ b/src/input/plugins/CdioParanoiaInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -31,7 +31,7 @@ #include "system/ByteOrder.hxx" #include "fs/AllocatedPath.hxx" #include "Log.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "config/ConfigError.hxx" #include <stdio.h> @@ -39,7 +39,6 @@ #include <stddef.h> #include <string.h> #include <stdlib.h> -#include <glib.h> #include <assert.h> #ifdef HAVE_CDIO_PARANOIA_PARANOIA_H @@ -107,9 +106,9 @@ static constexpr Domain cdio_domain("cdio"); static bool default_reverse_endian; static InputPlugin::InitResult -input_cdio_init(const config_param ¶m, Error &error) +input_cdio_init(const ConfigBlock &block, Error &error) { - const char *value = param.GetBlockValue("default_byte_order"); + const char *value = block.GetBlockValue("default_byte_order"); if (value != nullptr) { if (strcmp(value, "little_endian") == 0) default_reverse_endian = IsBigEndian(); @@ -149,7 +148,7 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, Error &error) const char *slash = strrchr(src, '/'); if (slash == nullptr) { /* play the whole CD in the specified drive */ - g_strlcpy(dest->device, src, sizeof(dest->device)); + CopyString(dest->device, src, sizeof(dest->device)); dest->track = -1; return true; } diff --git a/src/input/plugins/CdioParanoiaInputPlugin.hxx b/src/input/plugins/CdioParanoiaInputPlugin.hxx index e2804e8c7..a51b43827 100644 --- a/src/input/plugins/CdioParanoiaInputPlugin.hxx +++ b/src/input/plugins/CdioParanoiaInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index 3aa3b0018..814fe7a32 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,7 +23,7 @@ #include "../IcyInputStream.hxx" #include "../InputPlugin.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "tag/Tag.hxx" #include "tag/TagBuilder.hxx" #include "event/SocketMonitor.hxx" @@ -537,7 +537,7 @@ CurlMulti::OnTimeout() */ static InputPlugin::InitResult -input_curl_init(const config_param ¶m, Error &error) +input_curl_init(const ConfigBlock &block, Error &error) { CURLcode code = curl_global_init(CURL_GLOBAL_ALL); if (code != CURLE_OK) { @@ -559,22 +559,22 @@ input_curl_init(const config_param ¶m, Error &error) http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK"); - proxy = param.GetBlockValue("proxy"); - proxy_port = param.GetBlockValue("proxy_port", 0u); - proxy_user = param.GetBlockValue("proxy_user"); - proxy_password = param.GetBlockValue("proxy_password"); + proxy = block.GetBlockValue("proxy"); + proxy_port = block.GetBlockValue("proxy_port", 0u); + proxy_user = block.GetBlockValue("proxy_user"); + proxy_password = block.GetBlockValue("proxy_password"); if (proxy == nullptr) { /* deprecated proxy configuration */ - proxy = config_get_string(CONF_HTTP_PROXY_HOST, nullptr); - proxy_port = config_get_positive(CONF_HTTP_PROXY_PORT, 0); - proxy_user = config_get_string(CONF_HTTP_PROXY_USER, nullptr); - proxy_password = config_get_string(CONF_HTTP_PROXY_PASSWORD, + proxy = config_get_string(ConfigOption::HTTP_PROXY_HOST); + proxy_port = config_get_positive(ConfigOption::HTTP_PROXY_PORT, 0); + proxy_user = config_get_string(ConfigOption::HTTP_PROXY_USER); + proxy_password = config_get_string(ConfigOption::HTTP_PROXY_PASSWORD, ""); } - verify_peer = param.GetBlockValue("verify_peer", true); - verify_host = param.GetBlockValue("verify_host", true); + verify_peer = block.GetBlockValue("verify_peer", true); + verify_host = block.GetBlockValue("verify_host", true); CURLM *multi = curl_multi_init(); if (multi == nullptr) { @@ -651,7 +651,10 @@ CurlInputStream::HeaderReceived(const char *name, std::string &&value) return; size_t icy_metaint = ParseUint64(value.c_str()); +#ifndef WIN32 + /* Windows doesn't know "%z" */ FormatDebug(curl_domain, "icy-metaint=%zu", icy_metaint); +#endif if (icy_metaint > 0) { icy->Enable(icy_metaint); diff --git a/src/input/plugins/CurlInputPlugin.hxx b/src/input/plugins/CurlInputPlugin.hxx index 4acb18bfc..57bbe714b 100644 --- a/src/input/plugins/CurlInputPlugin.hxx +++ b/src/input/plugins/CurlInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/FfmpegInputPlugin.cxx b/src/input/plugins/FfmpegInputPlugin.cxx index 669f8d403..444273d90 100644 --- a/src/input/plugins/FfmpegInputPlugin.cxx +++ b/src/input/plugins/FfmpegInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "config.h" #include "FfmpegInputPlugin.hxx" +#include "lib/ffmpeg/Init.hxx" #include "lib/ffmpeg/Domain.hxx" #include "lib/ffmpeg/Error.hxx" #include "../InputStream.hxx" @@ -31,7 +32,6 @@ extern "C" { #include <libavformat/avio.h> -#include <libavformat/avformat.h> } struct FfmpegInputStream final : public InputStream { @@ -72,10 +72,10 @@ input_ffmpeg_supported(void) } static InputPlugin::InitResult -input_ffmpeg_init(gcc_unused const config_param ¶m, +input_ffmpeg_init(gcc_unused const ConfigBlock &block, Error &error) { - av_register_all(); + FfmpegInit(); /* disable this plugin if there's no registered protocol */ if (!input_ffmpeg_supported()) { diff --git a/src/input/plugins/FfmpegInputPlugin.hxx b/src/input/plugins/FfmpegInputPlugin.hxx index 43f829e89..40a834bdc 100644 --- a/src/input/plugins/FfmpegInputPlugin.hxx +++ b/src/input/plugins/FfmpegInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/FileInputPlugin.cxx b/src/input/plugins/FileInputPlugin.cxx index 867b5722d..aa4676470 100644 --- a/src/input/plugins/FileInputPlugin.cxx +++ b/src/input/plugins/FileInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,34 +23,30 @@ #include "../InputPlugin.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#include "fs/FileSystem.hxx" #include "fs/Path.hxx" -#include "system/fd_util.h" -#include "open.h" +#include "fs/FileInfo.hxx" +#include "fs/io/FileReader.hxx" +#include "system/FileDescriptor.hxx" #include <sys/stat.h> -#include <unistd.h> +#include <fcntl.h> #include <errno.h> static constexpr Domain file_domain("file"); class FileInputStream final : public InputStream { - const int fd; + FileReader reader; public: - FileInputStream(const char *path, int _fd, off_t _size, + FileInputStream(const char *path, FileReader &&_reader, off_t _size, Mutex &_mutex, Cond &_cond) :InputStream(path, _mutex, _cond), - fd(_fd) { + reader(std::move(_reader)) { size = _size; seekable = true; SetReady(); } - ~FileInputStream() { - close(fd); - } - /* virtual methods from InputStream */ bool IsEOF() override { @@ -66,32 +62,28 @@ OpenFileInputStream(Path path, Mutex &mutex, Cond &cond, Error &error) { - const int fd = OpenFile(path, O_RDONLY|O_BINARY, 0); - if (fd < 0) { - error.FormatErrno("Failed to open \"%s\"", - path.c_str()); + FileReader reader(path, error); + if (!reader.IsDefined()) return nullptr; - } - struct stat st; - if (fstat(fd, &st) < 0) { - error.FormatErrno("Failed to stat \"%s\"", path.c_str()); - close(fd); + FileInfo info; + if (!reader.GetFileInfo(info, error)) return nullptr; - } - if (!S_ISREG(st.st_mode)) { + if (!info.IsRegular()) { error.Format(file_domain, "Not a regular file: %s", path.c_str()); - close(fd); return nullptr; } #ifdef POSIX_FADV_SEQUENTIAL - posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL); + posix_fadvise(reader.GetFD().Get(), (off_t)0, info.GetSize(), + POSIX_FADV_SEQUENTIAL); #endif - return new FileInputStream(path.c_str(), fd, st.st_size, mutex, cond); + return new FileInputStream(path.ToUTF8().c_str(), + std::move(reader), info.GetSize(), + mutex, cond); } static InputStream * @@ -107,24 +99,19 @@ input_file_open(gcc_unused const char *filename, bool FileInputStream::Seek(offset_type new_offset, Error &error) { - auto result = lseek(fd, (off_t)new_offset, SEEK_SET); - if (result < 0) { - error.SetErrno("Failed to seek"); + if (!reader.Seek((off_t)new_offset, error)) return false; - } - offset = (offset_type)result; + offset = new_offset; return true; } size_t FileInputStream::Read(void *ptr, size_t read_size, Error &error) { - ssize_t nbytes = read(fd, ptr, read_size); - if (nbytes < 0) { - error.SetErrno("Failed to read"); + ssize_t nbytes = reader.Read(ptr, read_size, error); + if (nbytes < 0) return 0; - } offset += nbytes; return (size_t)nbytes; diff --git a/src/input/plugins/FileInputPlugin.hxx b/src/input/plugins/FileInputPlugin.hxx index ee194ec34..a00401c53 100644 --- a/src/input/plugins/FileInputPlugin.hxx +++ b/src/input/plugins/FileInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/MmsInputPlugin.cxx b/src/input/plugins/MmsInputPlugin.cxx index d01cff3b3..244dfd945 100644 --- a/src/input/plugins/MmsInputPlugin.cxx +++ b/src/input/plugins/MmsInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/MmsInputPlugin.hxx b/src/input/plugins/MmsInputPlugin.hxx index b4017ffd6..cf1b9ba65 100644 --- a/src/input/plugins/MmsInputPlugin.hxx +++ b/src/input/plugins/MmsInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/NfsInputPlugin.cxx b/src/input/plugins/NfsInputPlugin.cxx index c6c0970b9..077362c18 100644 --- a/src/input/plugins/NfsInputPlugin.cxx +++ b/src/input/plugins/NfsInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,10 +28,6 @@ #include "util/StringUtil.hxx" #include "util/Error.hxx" -extern "C" { -#include <nfsc/libnfs.h> -} - #include <string.h> #include <sys/stat.h> #include <fcntl.h> @@ -225,7 +221,7 @@ NfsInputStream::OnNfsFileError(Error &&error) */ static InputPlugin::InitResult -input_nfs_init(const config_param &, Error &) +input_nfs_init(const ConfigBlock &, Error &) { nfs_init(); return InputPlugin::InitResult::SUCCESS; diff --git a/src/input/plugins/NfsInputPlugin.hxx b/src/input/plugins/NfsInputPlugin.hxx index d2cc87549..5420ec967 100644 --- a/src/input/plugins/NfsInputPlugin.hxx +++ b/src/input/plugins/NfsInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/RewindInputPlugin.cxx b/src/input/plugins/RewindInputPlugin.cxx index 95f604044..cd027299c 100644 --- a/src/input/plugins/RewindInputPlugin.cxx +++ b/src/input/plugins/RewindInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/RewindInputPlugin.hxx b/src/input/plugins/RewindInputPlugin.hxx index 56b01b585..099921e7a 100644 --- a/src/input/plugins/RewindInputPlugin.hxx +++ b/src/input/plugins/RewindInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/input/plugins/SmbclientInputPlugin.cxx b/src/input/plugins/SmbclientInputPlugin.cxx index 399613720..ec6857c19 100644 --- a/src/input/plugins/SmbclientInputPlugin.cxx +++ b/src/input/plugins/SmbclientInputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -66,14 +66,14 @@ public: */ static InputPlugin::InitResult -input_smbclient_init(gcc_unused const config_param ¶m, Error &error) +input_smbclient_init(gcc_unused const ConfigBlock &block, Error &error) { if (!SmbclientInit(error)) return InputPlugin::InitResult::UNAVAILABLE; // TODO: create one global SMBCCTX here? - // TODO: evaluate config_param, call smbc_setOption*() + // TODO: evaluate ConfigBlock, call smbc_setOption*() return InputPlugin::InitResult::SUCCESS; } diff --git a/src/input/plugins/SmbclientInputPlugin.hxx b/src/input/plugins/SmbclientInputPlugin.hxx index a0539d020..31c55196c 100644 --- a/src/input/plugins/SmbclientInputPlugin.hxx +++ b/src/input/plugins/SmbclientInputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/expat/ExpatParser.cxx b/src/lib/expat/ExpatParser.cxx index c6b1abe76..7d9f1d587 100644 --- a/src/lib/expat/ExpatParser.cxx +++ b/src/lib/expat/ExpatParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/expat/ExpatParser.hxx b/src/lib/expat/ExpatParser.hxx index 9d2ac65e5..5f2626dda 100644 --- a/src/lib/expat/ExpatParser.hxx +++ b/src/lib/expat/ExpatParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,6 +41,9 @@ public: XML_ParserFree(parser); } + ExpatParser(const ExpatParser &) = delete; + ExpatParser &operator=(const ExpatParser &) = delete; + void SetElementHandler(XML_StartElementHandler start, XML_EndElementHandler end) { XML_SetElementHandler(parser, start, end); diff --git a/src/lib/ffmpeg/Buffer.hxx b/src/lib/ffmpeg/Buffer.hxx new file mode 100644 index 000000000..2463ce197 --- /dev/null +++ b/src/lib/ffmpeg/Buffer.hxx @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003-2015 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_FFMPEG_BUFFER_HXX +#define MPD_FFMPEG_BUFFER_HXX + +extern "C" { +#include <libavutil/mem.h> + +#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 18, 0) +#define HAVE_AV_FAST_MALLOC +#else +#include <libavcodec/avcodec.h> +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0) +#define HAVE_AV_FAST_MALLOC +#endif +#endif +} + +#include <stddef.h> + +/* suppress the ffmpeg compatibility macro */ +#ifdef SampleFormat +#undef SampleFormat +#endif + +class FfmpegBuffer { + void *data; + unsigned size; + +public: + FfmpegBuffer():data(nullptr), size(0) {} + + ~FfmpegBuffer() { + av_free(data); + } + + gcc_malloc + void *Get(size_t min_size) { +#ifdef HAVE_AV_FAST_MALLOC + av_fast_malloc(&data, &size, min_size); +#else + void *new_data = av_fast_realloc(data, &size, min_size); + if (new_data == nullptr) + return AVERROR(ENOMEM); + data = new_data; +#endif + return data; + } + + template<typename T> + T *GetT(size_t n) { + return (T *)Get(n * sizeof(T)); + } +}; + +#endif diff --git a/src/lib/ffmpeg/Domain.cxx b/src/lib/ffmpeg/Domain.cxx index 78db30bae..08b3c6b43 100644 --- a/src/lib/ffmpeg/Domain.cxx +++ b/src/lib/ffmpeg/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/ffmpeg/Domain.hxx b/src/lib/ffmpeg/Domain.hxx index f21498a32..c6d82f800 100644 --- a/src/lib/ffmpeg/Domain.hxx +++ b/src/lib/ffmpeg/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/ffmpeg/Error.cxx b/src/lib/ffmpeg/Error.cxx index bcc12fb1d..53f4d65f5 100644 --- a/src/lib/ffmpeg/Error.cxx +++ b/src/lib/ffmpeg/Error.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/ffmpeg/Error.hxx b/src/lib/ffmpeg/Error.hxx index 943dca6ce..a92394b2c 100644 --- a/src/lib/ffmpeg/Error.hxx +++ b/src/lib/ffmpeg/Error.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/ffmpeg/Init.cxx b/src/lib/ffmpeg/Init.cxx new file mode 100644 index 000000000..44c641f89 --- /dev/null +++ b/src/lib/ffmpeg/Init.cxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2003-2015 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. + */ + +/* necessary because libavutil/common.h uses UINT64_C */ +#define __STDC_CONSTANT_MACROS + +#include "config.h" +#include "Init.hxx" +#include "LogCallback.hxx" + +extern "C" { +#include <libavformat/avformat.h> +} + +void +FfmpegInit() +{ + av_log_set_callback(FfmpegLogCallback); + + av_register_all(); +} + diff --git a/src/tag/TagSettings.c b/src/lib/ffmpeg/Init.hxx index e0c577c2b..ca5f9d691 100644 --- a/src/tag/TagSettings.c +++ b/src/lib/ffmpeg/Init.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,9 +17,10 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "TagSettings.h" +#ifndef MPD_FFMPEG_INIT_HXX +#define MPD_FFMPEG_INIT_HXX -bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES] = { - /* ignore comments by default */ - [TAG_COMMENT] = true, -}; +void +FfmpegInit(); + +#endif diff --git a/src/lib/ffmpeg/LogCallback.cxx b/src/lib/ffmpeg/LogCallback.cxx new file mode 100644 index 000000000..ce2caeabb --- /dev/null +++ b/src/lib/ffmpeg/LogCallback.cxx @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2003-2015 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. + */ + +/* necessary because libavutil/common.h uses UINT64_C */ +#define __STDC_CONSTANT_MACROS + +#include "config.h" +#include "LogCallback.hxx" +#include "Domain.hxx" +#include "LogV.hxx" +#include "util/Domain.hxx" + +extern "C" { +#include <libavutil/log.h> +} + +#include <stdio.h> + +gcc_const +static LogLevel +FfmpegImportLogLevel(int level) +{ + if (level <= AV_LOG_FATAL) + return LogLevel::ERROR; + + if (level <= AV_LOG_WARNING) + return LogLevel::WARNING; + + if (level <= AV_LOG_INFO) + return LogLevel::INFO; + + return LogLevel::DEBUG; +} + +void +FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl) +{ + const AVClass * cls = nullptr; + + if (ptr != nullptr) + cls = *(const AVClass *const*)ptr; + + if (cls != nullptr) { + char domain[64]; + snprintf(domain, sizeof(domain), "%s/%s", + ffmpeg_domain.GetName(), cls->item_name(ptr)); + const Domain d(domain); + LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl); + } +} diff --git a/src/lib/ffmpeg/LogCallback.hxx b/src/lib/ffmpeg/LogCallback.hxx new file mode 100644 index 000000000..f1b114366 --- /dev/null +++ b/src/lib/ffmpeg/LogCallback.hxx @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2003-2015 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_FFMPEG_LOG_CALLBACK_HXX +#define MPD_FFMPEG_LOG_CALLBACK_HXX + +#include "check.h" + +#include <stdarg.h> + +void +FfmpegLogCallback(void *ptr, int level, const char *fmt, va_list vl); + +#endif diff --git a/src/lib/ffmpeg/LogError.cxx b/src/lib/ffmpeg/LogError.cxx new file mode 100644 index 000000000..8a0675a1c --- /dev/null +++ b/src/lib/ffmpeg/LogError.cxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2015 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 "LogError.hxx" +#include "Domain.hxx" +#include "Log.hxx" + +#include <cstdint> /* needed due to libavutil bug */ + +extern "C" { +#include <libavutil/error.h> +} + +void +LogFfmpegError(int errnum) +{ + char msg[256]; + av_strerror(errnum, msg, sizeof(msg)); + LogError(ffmpeg_domain, msg); +} + +void +LogFfmpegError(int errnum, const char *prefix) +{ + char msg[256]; + av_strerror(errnum, msg, sizeof(msg)); + FormatError(ffmpeg_domain, "%s: %s", prefix, msg); +} diff --git a/src/lib/ffmpeg/LogError.hxx b/src/lib/ffmpeg/LogError.hxx new file mode 100644 index 000000000..e6d96988d --- /dev/null +++ b/src/lib/ffmpeg/LogError.hxx @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2003-2015 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_FFMPEG_LOG_ERROR_HXX +#define MPD_FFMPEG_LOG_ERROR_HXX + +void +LogFfmpegError(int errnum); + +void +LogFfmpegError(int errnum, const char *prefix); + +#endif diff --git a/src/lib/ffmpeg/Time.hxx b/src/lib/ffmpeg/Time.hxx new file mode 100644 index 000000000..92c076d0d --- /dev/null +++ b/src/lib/ffmpeg/Time.hxx @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2003-2015 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_FFMPEG_TIME_HXX +#define MPD_FFMPEG_TIME_HXX + +#include "Chrono.hxx" +#include "Compiler.h" + +extern "C" { +#include <libavutil/avutil.h> +#include <libavutil/mathematics.h> +} + +#include <assert.h> +#include <stdint.h> + +/* suppress the ffmpeg compatibility macro */ +#ifdef SampleFormat +#undef SampleFormat +#endif + +/** + * Convert a FFmpeg time stamp to a floating point value (in seconds). + */ +gcc_const +static inline double +FfmpegTimeToDouble(int64_t t, const AVRational time_base) +{ + assert(t != (int64_t)AV_NOPTS_VALUE); + + return (double)av_rescale_q(t, time_base, (AVRational){1, 1024}) + / (double)1024; +} + +/** + * Convert a std::ratio to a #AVRational. + */ +template<typename Ratio> +static inline constexpr AVRational +RatioToAVRational() +{ + return { Ratio::num, Ratio::den }; +} + +/** + * Convert a FFmpeg time stamp to a #SongTime. + */ +gcc_const +static inline SongTime +FromFfmpegTime(int64_t t, const AVRational time_base) +{ + assert(t != (int64_t)AV_NOPTS_VALUE); + + return SongTime::FromMS(av_rescale_q(t, time_base, + (AVRational){1, 1000})); +} + +/** + * Convert a FFmpeg time stamp to a #SignedSongTime. + */ +gcc_const +static inline SignedSongTime +FromFfmpegTimeChecked(int64_t t, const AVRational time_base) +{ + return t != (int64_t)AV_NOPTS_VALUE + ? SignedSongTime(FromFfmpegTime(t, time_base)) + : SignedSongTime::Negative(); +} + +/** + * Convert a #SongTime to a FFmpeg time stamp with the given base. + */ +gcc_const +static inline int64_t +ToFfmpegTime(SongTime t, const AVRational time_base) +{ + return av_rescale_q(t.count(), + RatioToAVRational<SongTime::period>(), + time_base); +} + +/** + * Replace #AV_NOPTS_VALUE with the given fallback. + */ +static constexpr int64_t +FfmpegTimestampFallback(int64_t t, int64_t fallback) +{ + return gcc_likely(t != int64_t(AV_NOPTS_VALUE)) + ? t + : fallback; +} + +#endif diff --git a/src/lib/icu/Collate.cxx b/src/lib/icu/Collate.cxx index 17b536b37..dc8598a7a 100644 --- a/src/lib/icu/Collate.cxx +++ b/src/lib/icu/Collate.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,8 +19,10 @@ #include "config.h" #include "Collate.hxx" +#include "util/AllocatedString.hxx" #ifdef HAVE_ICU +#include "Util.hxx" #include "Error.hxx" #include "util/WritableBuffer.hxx" #include "util/ConstBuffer.hxx" @@ -29,13 +31,17 @@ #include <unicode/ucol.h> #include <unicode/ustring.h> -#elif defined(HAVE_GLIB) -#include <glib.h> #else #include <algorithm> #include <ctype.h> #endif +#ifdef WIN32 +#include "Win32.hxx" +#include "util/AllocatedString.hxx" +#include <windows.h> +#endif + #include <assert.h> #include <string.h> #include <strings.h> @@ -71,50 +77,6 @@ IcuCollateFinish() ucol_close(collator); } -static WritableBuffer<UChar> -UCharFromUTF8(const char *src) -{ - assert(src != nullptr); - - const size_t src_length = strlen(src); - const size_t dest_capacity = src_length; - UChar *dest = new UChar[dest_capacity]; - - UErrorCode error_code = U_ZERO_ERROR; - int32_t dest_length; - u_strFromUTF8(dest, dest_capacity, &dest_length, - src, src_length, - &error_code); - if (U_FAILURE(error_code)) { - delete[] dest; - return nullptr; - } - - return { dest, size_t(dest_length) }; -} - -static WritableBuffer<char> -UCharToUTF8(ConstBuffer<UChar> src) -{ - assert(!src.IsNull()); - - /* worst-case estimate */ - size_t dest_capacity = 4 * src.size; - - char *dest = new char[dest_capacity]; - - UErrorCode error_code = U_ZERO_ERROR; - int32_t dest_length; - u_strToUTF8(dest, dest_capacity, &dest_length, src.data, src.size, - &error_code); - if (U_FAILURE(error_code)) { - delete[] dest; - return nullptr; - } - - return { dest, size_t(dest_length) }; -} - #endif gcc_pure @@ -150,14 +112,32 @@ IcuCollate(const char *a, const char *b) return result; #endif -#elif defined(HAVE_GLIB) - return g_utf8_collate(a, b); +#elif defined(WIN32) + const auto wa = MultiByteToWideChar(CP_UTF8, a); + const auto wb = MultiByteToWideChar(CP_UTF8, b); + if (wa.IsNull()) + return wb.IsNull() ? 0 : -1; + else if (wb.IsNull()) + return 1; + + auto result = CompareStringEx(LOCALE_NAME_INVARIANT, + LINGUISTIC_IGNORECASE, + wa.c_str(), -1, + wb.c_str(), -1, + nullptr, nullptr, 0); + if (result != 0) + /* "To maintain the C runtime convention of comparing + strings, the value 2 can be subtracted from a + nonzero return value." */ + result -= 2; + + return result; #else - return strcasecmp(a, b); + return strcoll(a, b); #endif } -std::string +AllocatedString<> IcuCaseFold(const char *src) { #ifdef HAVE_ICU @@ -169,37 +149,70 @@ IcuCaseFold(const char *src) const auto u = UCharFromUTF8(src); if (u.IsNull()) - return std::string(src); + return AllocatedString<>::Duplicate(src); size_t folded_capacity = u.size * 2u; UChar *folded = new UChar[folded_capacity]; UErrorCode error_code = U_ZERO_ERROR; size_t folded_length = u_strFoldCase(folded, folded_capacity, - u.data, u.size, - U_FOLD_CASE_DEFAULT, - &error_code); + u.data, u.size, + U_FOLD_CASE_DEFAULT, + &error_code); delete[] u.data; if (folded_length == 0 || error_code != U_ZERO_ERROR) { delete[] folded; - return std::string(src); + return AllocatedString<>::Duplicate(src); } - auto result2 = UCharToUTF8({folded, folded_length}); + auto result = UCharToUTF8({folded, folded_length}); delete[] folded; - if (result2.IsNull()) - return std::string(src); - - std::string result(result2.data, result2.size); - delete[] result2.data; -#elif defined(HAVE_GLIB) - char *tmp = g_utf8_casefold(src, -1); - std::string result(tmp); - g_free(tmp); + return result; + +#elif defined(WIN32) + const auto u = MultiByteToWideChar(CP_UTF8, src); + if (u.IsNull()) + return AllocatedString<>::Duplicate(src); + + const int size = LCMapStringEx(LOCALE_NAME_INVARIANT, + LCMAP_SORTKEY|LINGUISTIC_IGNORECASE, + u.c_str(), -1, nullptr, 0, + nullptr, nullptr, 0); + if (size <= 0) + return AllocatedString<>::Duplicate(src); + + auto buffer = new wchar_t[size]; + if (LCMapStringEx(LOCALE_NAME_INVARIANT, + LCMAP_SORTKEY|LINGUISTIC_IGNORECASE, + u.c_str(), -1, buffer, size, + nullptr, nullptr, 0) <= 0) { + delete[] buffer; + return AllocatedString<>::Duplicate(src); + } + + auto result = WideCharToMultiByte(CP_UTF8, buffer); + delete[] buffer; + if (result.IsNull()) + return AllocatedString<>::Duplicate(src); + + return result; + #else - std::string result(src); - std::transform(result.begin(), result.end(), result.begin(), tolower); + size_t size = strlen(src) + 1; + auto buffer = new char[size]; + size_t nbytes = strxfrm(buffer, src, size); + if (nbytes >= size) { + /* buffer too small - reallocate and try again */ + delete[] buffer; + size = nbytes + 1; + buffer = new char[size]; + nbytes = strxfrm(buffer, src, size); + } + + assert(nbytes < size); + assert(buffer[nbytes] == 0); + + return AllocatedString<>::Donate(buffer); #endif - return result; } diff --git a/src/lib/icu/Collate.hxx b/src/lib/icu/Collate.hxx index 8ae8de46a..0ad3b24ff 100644 --- a/src/lib/icu/Collate.hxx +++ b/src/lib/icu/Collate.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,6 +26,7 @@ #include <string> class Error; +template<typename T> class AllocatedString; bool IcuCollateInit(Error &error); @@ -38,7 +39,7 @@ int IcuCollate(const char *a, const char *b); gcc_pure gcc_nonnull_all -std::string +AllocatedString<char> IcuCaseFold(const char *src); #endif diff --git a/src/lib/icu/Converter.cxx b/src/lib/icu/Converter.cxx new file mode 100644 index 000000000..61c0cbdd5 --- /dev/null +++ b/src/lib/icu/Converter.cxx @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2003-2015 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 "Converter.hxx" +#include "Error.hxx" +#include "util/Error.hxx" +#include "util/Macros.hxx" +#include "util/AllocatedString.hxx" +#include "util/WritableBuffer.hxx" +#include "util/ConstBuffer.hxx" + +#include <string.h> + +#ifdef HAVE_ICU +#include "Util.hxx" +#include <unicode/ucnv.h> +#elif defined(HAVE_ICONV) +#include "util/Domain.hxx" +static constexpr Domain iconv_domain("iconv"); +#endif + +#ifdef HAVE_ICU + +IcuConverter::~IcuConverter() +{ + ucnv_close(converter); +} + +#endif + +#ifdef HAVE_ICU_CONVERTER + +IcuConverter * +IcuConverter::Create(const char *charset, Error &error) +{ +#ifdef HAVE_ICU + UErrorCode code = U_ZERO_ERROR; + UConverter *converter = ucnv_open(charset, &code); + if (converter == nullptr) { + error.Format(icu_domain, int(code), + "Failed to initialize charset '%s': %s", + charset, u_errorName(code)); + return nullptr; + } + + return new IcuConverter(converter); +#elif defined(HAVE_ICONV) + iconv_t to = iconv_open("utf-8", charset); + iconv_t from = iconv_open(charset, "utf-8"); + if (to == (iconv_t)-1 || from == (iconv_t)-1) { + error.FormatErrno("Failed to initialize charset '%s'", + charset); + if (to != (iconv_t)-1) + iconv_close(to); + if (from != (iconv_t)-1) + iconv_close(from); + return nullptr; + } + + return new IcuConverter(to, from); +#endif +} + +#ifdef HAVE_ICU +#elif defined(HAVE_ICONV) + +static AllocatedString<char> +DoConvert(iconv_t conv, const char *src) +{ + // TODO: dynamic buffer? + char buffer[4096]; + char *in = const_cast<char *>(src); + char *out = buffer; + size_t in_left = strlen(src); + size_t out_left = sizeof(buffer); + + size_t n = iconv(conv, &in, &in_left, &out, &out_left); + + if (n == static_cast<size_t>(-1) || in_left > 0) + return nullptr; + + return AllocatedString<>::Duplicate(buffer, sizeof(buffer) - out_left); +} + +#endif + +AllocatedString<char> +IcuConverter::ToUTF8(const char *s) const +{ +#ifdef HAVE_ICU + const ScopeLock protect(mutex); + + ucnv_resetToUnicode(converter); + + // TODO: dynamic buffer? + UChar buffer[4096], *target = buffer; + const char *source = s; + + UErrorCode code = U_ZERO_ERROR; + + ucnv_toUnicode(converter, &target, buffer + ARRAY_SIZE(buffer), + &source, source + strlen(source), + nullptr, true, &code); + if (code != U_ZERO_ERROR) + return nullptr; + + const size_t target_length = target - buffer; + return UCharToUTF8({buffer, target_length}); +#elif defined(HAVE_ICONV) + return DoConvert(to_utf8, s); +#endif +} + +AllocatedString<char> +IcuConverter::FromUTF8(const char *s) const +{ +#ifdef HAVE_ICU + const ScopeLock protect(mutex); + + const auto u = UCharFromUTF8(s); + if (u.IsNull()) + return nullptr; + + ucnv_resetFromUnicode(converter); + + // TODO: dynamic buffer? + char buffer[4096], *target = buffer; + const UChar *source = u.data; + UErrorCode code = U_ZERO_ERROR; + + ucnv_fromUnicode(converter, &target, buffer + ARRAY_SIZE(buffer), + &source, u.end(), + nullptr, true, &code); + delete[] u.data; + + if (code != U_ZERO_ERROR) + return nullptr; + + return AllocatedString<>::Duplicate(buffer, target); + +#elif defined(HAVE_ICONV) + return DoConvert(from_utf8, s); +#endif +} + +#endif diff --git a/src/lib/icu/Converter.hxx b/src/lib/icu/Converter.hxx new file mode 100644 index 000000000..edb092d8f --- /dev/null +++ b/src/lib/icu/Converter.hxx @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2003-2015 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_ICU_CONVERTER_HXX +#define MPD_ICU_CONVERTER_HXX + +#include "check.h" +#include "Compiler.h" + +#ifdef HAVE_ICU +#include "thread/Mutex.hxx" +#define HAVE_ICU_CONVERTER +#elif defined(HAVE_ICONV) +#include <iconv.h> +#define HAVE_ICU_CONVERTER +#endif + +#ifdef HAVE_ICU_CONVERTER + +class Error; + +#ifdef HAVE_ICU +struct UConverter; +#endif + +template<typename T> class AllocatedString; + +/** + * This class can convert strings with a certain character set to and + * from UTF-8. + */ +class IcuConverter { +#ifdef HAVE_ICU + /** + * ICU's UConverter class is not thread-safe. This mutex + * serializes simultaneous calls. + */ + mutable Mutex mutex; + + UConverter *const converter; + + IcuConverter(UConverter *_converter):converter(_converter) {} +#elif defined(HAVE_ICONV) + const iconv_t to_utf8, from_utf8; + + IcuConverter(iconv_t _to, iconv_t _from) + :to_utf8(_to), from_utf8(_from) {} +#endif + +public: +#ifdef HAVE_ICU + ~IcuConverter(); +#elif defined(HAVE_ICONV) + ~IcuConverter() { + iconv_close(to_utf8); + iconv_close(from_utf8); + } +#endif + + static IcuConverter *Create(const char *charset, Error &error); + + /** + * Convert the string to UTF-8. + * + * Returns AllocatedString::Null() on error. + */ + gcc_pure gcc_nonnull_all + AllocatedString<char> ToUTF8(const char *s) const; + + /** + * Convert the string from UTF-8. + * + * Returns AllocatedString::Null() on error. + */ + gcc_pure gcc_nonnull_all + AllocatedString<char> FromUTF8(const char *s) const; +}; + +#endif + +#endif diff --git a/src/lib/icu/Error.cxx b/src/lib/icu/Error.cxx index 1fef078ac..f49ede352 100644 --- a/src/lib/icu/Error.cxx +++ b/src/lib/icu/Error.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/icu/Error.hxx b/src/lib/icu/Error.hxx index e96667f57..37cdb12fe 100644 --- a/src/lib/icu/Error.hxx +++ b/src/lib/icu/Error.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/icu/Init.cxx b/src/lib/icu/Init.cxx index 1d0ad0777..6b70d60ee 100644 --- a/src/lib/icu/Init.cxx +++ b/src/lib/icu/Init.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/icu/Init.hxx b/src/lib/icu/Init.hxx index 9f585e2bd..402e7b957 100644 --- a/src/lib/icu/Init.hxx +++ b/src/lib/icu/Init.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/icu/Util.cxx b/src/lib/icu/Util.cxx new file mode 100644 index 000000000..92f1de5aa --- /dev/null +++ b/src/lib/icu/Util.cxx @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2003-2015 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 "Util.hxx" +#include "util/AllocatedString.hxx" +#include "util/WritableBuffer.hxx" +#include "util/ConstBuffer.hxx" + +#include <unicode/ustring.h> + +#include <assert.h> +#include <string.h> + +WritableBuffer<UChar> +UCharFromUTF8(const char *src) +{ + assert(src != nullptr); + + const size_t src_length = strlen(src); + const size_t dest_capacity = src_length; + UChar *dest = new UChar[dest_capacity]; + + UErrorCode error_code = U_ZERO_ERROR; + int32_t dest_length; + u_strFromUTF8(dest, dest_capacity, &dest_length, + src, src_length, + &error_code); + if (U_FAILURE(error_code)) { + delete[] dest; + return nullptr; + } + + return { dest, size_t(dest_length) }; +} + +AllocatedString<> +UCharToUTF8(ConstBuffer<UChar> src) +{ + assert(!src.IsNull()); + + /* worst-case estimate */ + size_t dest_capacity = 4 * src.size; + + char *dest = new char[dest_capacity + 1]; + + UErrorCode error_code = U_ZERO_ERROR; + int32_t dest_length; + u_strToUTF8(dest, dest_capacity, &dest_length, src.data, src.size, + &error_code); + if (U_FAILURE(error_code)) { + delete[] dest; + return nullptr; + } + + dest[dest_length] = 0; + return AllocatedString<>::Donate(dest); +} diff --git a/src/lib/icu/Util.hxx b/src/lib/icu/Util.hxx new file mode 100644 index 000000000..f2d99d0e6 --- /dev/null +++ b/src/lib/icu/Util.hxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2015 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_ICU_UTIL_HXX +#define MPD_ICU_UTIL_HXX + +#include "check.h" + +#include <unicode/utypes.h> + +template<typename T> struct WritableBuffer; +template<typename T> struct ConstBuffer; +template<typename T> class AllocatedString; + +/** + * Wrapper for u_strFromUTF8(). The returned pointer must be freed + * with delete[]. + */ +WritableBuffer<UChar> +UCharFromUTF8(const char *src); + +/** + * Wrapper for u_strToUTF8(). The returned pointer must be freed with + * delete[]. + */ +AllocatedString<char> +UCharToUTF8(ConstBuffer<UChar> src); + +#endif diff --git a/src/lib/icu/Win32.cxx b/src/lib/icu/Win32.cxx new file mode 100644 index 000000000..6f190c924 --- /dev/null +++ b/src/lib/icu/Win32.cxx @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2003-2015 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 "Win32.hxx" +#include "util/AllocatedString.hxx" + +#include <windows.h> + +AllocatedString<char> +WideCharToMultiByte(unsigned code_page, const wchar_t *src) +{ + int length = WideCharToMultiByte(code_page, 0, src, -1, nullptr, 0, + nullptr, nullptr); + if (length <= 0) + return nullptr; + + char *buffer = new char[length]; + length = WideCharToMultiByte(code_page, 0, src, -1, buffer, length, + nullptr, nullptr); + if (length <= 0) { + delete[] buffer; + return nullptr; + } + + return AllocatedString<char>::Donate(buffer); +} + +AllocatedString<wchar_t> +MultiByteToWideChar(unsigned code_page, const char *src) +{ + int length = MultiByteToWideChar(code_page, 0, src, -1, nullptr, 0); + if (length <= 0) + return nullptr; + + wchar_t *buffer = new wchar_t[length]; + length = MultiByteToWideChar(code_page, 0, src, -1, buffer, length); + if (length <= 0) { + delete[] buffer; + return nullptr; + } + + return AllocatedString<wchar_t>::Donate(buffer); +} diff --git a/src/lib/icu/Win32.hxx b/src/lib/icu/Win32.hxx new file mode 100644 index 000000000..253fe169a --- /dev/null +++ b/src/lib/icu/Win32.hxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2003-2015 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_ICU_WIN32_HXX +#define MPD_ICU_WIN32_HXX + +#include "check.h" +#include "Compiler.h" + +#include <wchar.h> + +template<typename T> class AllocatedString; + +gcc_pure gcc_nonnull_all +AllocatedString<char> +WideCharToMultiByte(unsigned code_page, const wchar_t *src); + +gcc_pure gcc_nonnull_all +AllocatedString<wchar_t> +MultiByteToWideChar(unsigned code_page, const char *src); + +#endif diff --git a/src/lib/nfs/Base.cxx b/src/lib/nfs/Base.cxx index 3004cd11b..588176ef6 100644 --- a/src/lib/nfs/Base.cxx +++ b/src/lib/nfs/Base.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Base.hxx b/src/lib/nfs/Base.hxx index 3a92a86d3..e007bfbd2 100644 --- a/src/lib/nfs/Base.hxx +++ b/src/lib/nfs/Base.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Blocking.cxx b/src/lib/nfs/Blocking.cxx index 58eaf6af2..7bccfa532 100644 --- a/src/lib/nfs/Blocking.cxx +++ b/src/lib/nfs/Blocking.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Blocking.hxx b/src/lib/nfs/Blocking.hxx index eb16dfb8c..47721363c 100644 --- a/src/lib/nfs/Blocking.hxx +++ b/src/lib/nfs/Blocking.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Callback.hxx b/src/lib/nfs/Callback.hxx index ae82ecc3c..849fbfbb9 100644 --- a/src/lib/nfs/Callback.hxx +++ b/src/lib/nfs/Callback.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Cancellable.hxx b/src/lib/nfs/Cancellable.hxx index 151be0528..6c207d9b2 100644 --- a/src/lib/nfs/Cancellable.hxx +++ b/src/lib/nfs/Cancellable.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx index 6e9f77345..3b3358be0 100644 --- a/src/lib/nfs/Connection.cxx +++ b/src/lib/nfs/Connection.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Connection.hxx b/src/lib/nfs/Connection.hxx index 3969a7e8f..3402116b7 100644 --- a/src/lib/nfs/Connection.hxx +++ b/src/lib/nfs/Connection.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Domain.cxx b/src/lib/nfs/Domain.cxx index fefe0dbf3..af79e45a8 100644 --- a/src/lib/nfs/Domain.cxx +++ b/src/lib/nfs/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Domain.hxx b/src/lib/nfs/Domain.hxx index 6730b92e1..15856657f 100644 --- a/src/lib/nfs/Domain.hxx +++ b/src/lib/nfs/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/FileReader.cxx b/src/lib/nfs/FileReader.cxx index 1b80f2c86..97522321b 100644 --- a/src/lib/nfs/FileReader.cxx +++ b/src/lib/nfs/FileReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/FileReader.hxx b/src/lib/nfs/FileReader.hxx index 1495a2832..5e3b5221f 100644 --- a/src/lib/nfs/FileReader.hxx +++ b/src/lib/nfs/FileReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Glue.cxx b/src/lib/nfs/Glue.cxx index 6e1e0f99b..fa894f59a 100644 --- a/src/lib/nfs/Glue.cxx +++ b/src/lib/nfs/Glue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Glue.hxx b/src/lib/nfs/Glue.hxx index 6da8957cb..d661b3fe0 100644 --- a/src/lib/nfs/Glue.hxx +++ b/src/lib/nfs/Glue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Lease.hxx b/src/lib/nfs/Lease.hxx index 6f88acf53..3276cfc31 100644 --- a/src/lib/nfs/Lease.hxx +++ b/src/lib/nfs/Lease.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/nfs/Manager.cxx b/src/lib/nfs/Manager.cxx index 6d50cce18..1cbf18ff1 100644 --- a/src/lib/nfs/Manager.cxx +++ b/src/lib/nfs/Manager.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,7 @@ #include "Manager.hxx" #include "event/Loop.hxx" #include "Log.hxx" +#include "util/DeleteDisposer.hxx" #include <string.h> @@ -65,9 +66,7 @@ NfsManager::~NfsManager() CollectGarbage(); - connections.clear_and_dispose([](ManagedConnection *c){ - delete c; - }); + connections.clear_and_dispose(DeleteDisposer()); } NfsConnection & @@ -95,9 +94,7 @@ NfsManager::CollectGarbage() { assert(GetEventLoop().IsInside()); - garbage.clear_and_dispose([](ManagedConnection *c){ - delete c; - }); + garbage.clear_and_dispose(DeleteDisposer()); } void diff --git a/src/lib/nfs/Manager.hxx b/src/lib/nfs/Manager.hxx index 130c81aca..1eb01590a 100644 --- a/src/lib/nfs/Manager.hxx +++ b/src/lib/nfs/Manager.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/pulse/Domain.cxx b/src/lib/pulse/Domain.cxx new file mode 100644 index 000000000..ac4821cae --- /dev/null +++ b/src/lib/pulse/Domain.cxx @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2015 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 "Domain.hxx" +#include "util/Domain.hxx" + +const Domain pulse_domain("pulse"); diff --git a/test/stdbin.h b/src/lib/pulse/Domain.hxx index 8b5502e6f..bacf8b7eb 100644 --- a/test/stdbin.h +++ b/src/lib/pulse/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,13 +17,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_STDBIN_H -#define MPD_STDBIN_H +#ifndef MPD_PULSE_DOMAIN_HXX +#define MPD_PULSE_DOMAIN_HXX -#ifdef WIN32 -#include <fcntl.h> -/** set binary mode on stdin/stdout */ -int _CRT_fmode = _O_BINARY; -#endif +class Domain; + +extern const Domain pulse_domain; #endif diff --git a/src/lib/pulse/Error.cxx b/src/lib/pulse/Error.cxx new file mode 100644 index 000000000..60ca0198a --- /dev/null +++ b/src/lib/pulse/Error.cxx @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2003-2015 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 "Error.hxx" +#include "Domain.hxx" +#include "util/Error.hxx" + +#include <pulse/context.h> +#include <pulse/error.h> + +void +SetPulseError(Error &error, pa_context *context, const char *prefix) +{ + const int e = pa_context_errno(context); + error.Format(pulse_domain, e, "%s: %s", prefix, pa_strerror(e)); +} diff --git a/src/lib/pulse/Error.hxx b/src/lib/pulse/Error.hxx new file mode 100644 index 000000000..c9225a7c2 --- /dev/null +++ b/src/lib/pulse/Error.hxx @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2003-2015 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_PULSE_ERROR_HXX +#define MPD_PULSE_ERROR_HXX + +class Error; +struct pa_context; + +void +SetPulseError(Error &error, pa_context *context, const char *prefix); + +#endif diff --git a/src/lib/pulse/LogError.cxx b/src/lib/pulse/LogError.cxx new file mode 100644 index 000000000..a322e7e75 --- /dev/null +++ b/src/lib/pulse/LogError.cxx @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2003-2015 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 "LogError.hxx" +#include "Domain.hxx" +#include "Log.hxx" + +#include <pulse/context.h> +#include <pulse/error.h> + +void +LogPulseError(pa_context *context, const char *prefix) +{ + const int e = pa_context_errno(context); + FormatError(pulse_domain, "%s: %s", prefix, pa_strerror(e)); +} diff --git a/src/tag/TagSettings.h b/src/lib/pulse/LogError.hxx index 33f89d4be..2e0859366 100644 --- a/src/tag/TagSettings.h +++ b/src/lib/pulse/LogError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -17,13 +17,12 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef MPD_TAG_SETTINGS_H -#define MPD_TAG_SETTINGS_H +#ifndef MPD_PULSE_LOG_ERROR_HXX +#define MPD_PULSE_LOG_ERROR_HXX -#include "TagType.h" +struct pa_context; -#include <stdbool.h> - -extern bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES]; +void +LogPulseError(pa_context *context, const char *prefix); #endif diff --git a/src/lib/smbclient/Domain.cxx b/src/lib/smbclient/Domain.cxx index 00f5ee6c1..c6f6b143d 100644 --- a/src/lib/smbclient/Domain.cxx +++ b/src/lib/smbclient/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/smbclient/Domain.hxx b/src/lib/smbclient/Domain.hxx index 3b21c4e60..dc9812fed 100644 --- a/src/lib/smbclient/Domain.hxx +++ b/src/lib/smbclient/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/smbclient/Init.cxx b/src/lib/smbclient/Init.cxx index a7f2da4dd..999e60fcd 100644 --- a/src/lib/smbclient/Init.cxx +++ b/src/lib/smbclient/Init.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/smbclient/Init.hxx b/src/lib/smbclient/Init.hxx index 21014ec8d..1ccaec033 100644 --- a/src/lib/smbclient/Init.hxx +++ b/src/lib/smbclient/Init.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/smbclient/Mutex.cxx b/src/lib/smbclient/Mutex.cxx index 4dfc5a9d3..fd78e9948 100644 --- a/src/lib/smbclient/Mutex.cxx +++ b/src/lib/smbclient/Mutex.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/smbclient/Mutex.hxx b/src/lib/smbclient/Mutex.hxx index dc7372e6e..893f6204d 100644 --- a/src/lib/smbclient/Mutex.hxx +++ b/src/lib/smbclient/Mutex.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/sqlite/Domain.cxx b/src/lib/sqlite/Domain.cxx new file mode 100644 index 000000000..4f6fe4c45 --- /dev/null +++ b/src/lib/sqlite/Domain.cxx @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2003-2015 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 "Domain.hxx" +#include "util/Domain.hxx" + +const Domain sqlite_domain("sqlite"); diff --git a/src/lib/sqlite/Domain.hxx b/src/lib/sqlite/Domain.hxx new file mode 100644 index 000000000..0b9965025 --- /dev/null +++ b/src/lib/sqlite/Domain.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2015 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_SQLITE_DOMAIN_HXX +#define MPD_SQLITE_DOMAIN_HXX + +class Domain; + +extern const Domain sqlite_domain; + +#endif diff --git a/src/lib/sqlite/Util.hxx b/src/lib/sqlite/Util.hxx new file mode 100644 index 000000000..151eac6c2 --- /dev/null +++ b/src/lib/sqlite/Util.hxx @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2003-2015 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_SQLITE_UTIL_HXX +#define MPD_SQLITE_UTIL_HXX + +#include "Domain.hxx" +#include "util/Error.hxx" + +#include <sqlite3.h> + +#include <assert.h> + +static void +SetError(Error &error, sqlite3 *db, int code, const char *msg) +{ + error.Format(sqlite_domain, code, "%s: %s", + msg, sqlite3_errmsg(db)); +} + +static void +SetError(Error &error, sqlite3_stmt *stmt, int code, const char *msg) +{ + SetError(error, sqlite3_db_handle(stmt), code, msg); +} + +static bool +Bind(sqlite3_stmt *stmt, unsigned i, const char *value, Error &error) +{ + int result = sqlite3_bind_text(stmt, i, value, -1, nullptr); + if (result != SQLITE_OK) { + SetError(error, stmt, result, "sqlite3_bind_text() failed"); + return false; + } + + return true; +} + +template<typename... Args> +static bool +BindAll2(gcc_unused Error &error, gcc_unused sqlite3_stmt *stmt, + gcc_unused unsigned i) +{ + assert(int(i - 1) == sqlite3_bind_parameter_count(stmt)); + + return true; +} + +template<typename... Args> +static bool +BindAll2(Error &error, sqlite3_stmt *stmt, unsigned i, + const char *value, Args&&... args) +{ + return Bind(stmt, i, value, error) && + BindAll2(error, stmt, i + 1, std::forward<Args>(args)...); +} + +template<typename... Args> +static bool +BindAll(Error &error, sqlite3_stmt *stmt, Args&&... args) +{ + assert(int(sizeof...(args)) == sqlite3_bind_parameter_count(stmt)); + + return BindAll2(error, stmt, 1, std::forward<Args>(args)...); +} + +/** + * Wrapper for BindAll() that returns the specified sqlite3_stmt* on + * success and nullptr on error. + */ +template<typename... Args> +static sqlite3_stmt * +BindAllOrNull(Error &error, sqlite3_stmt *stmt, Args&&... args) +{ + return BindAll(error, stmt, std::forward<Args>(args)...) + ? stmt + : nullptr; +} + +/** + * Call sqlite3_stmt() repepatedly until something other than + * SQLITE_BUSY is returned. + */ +static int +ExecuteBusy(sqlite3_stmt *stmt) +{ + int result; + do { + result = sqlite3_step(stmt); + } while (result == SQLITE_BUSY); + + return result; +} + +/** + * Wrapper for ExecuteBusy() that returns true on SQLITE_ROW. + */ +static bool +ExecuteRow(sqlite3_stmt *stmt, Error &error) +{ + int result = ExecuteBusy(stmt); + if (result == SQLITE_ROW) + return true; + + if (result != SQLITE_DONE) + SetError(error, stmt, result, "sqlite3_step() failed"); + + return false; +} + +/** + * Wrapper for ExecuteBusy() that interprets everything other than + * SQLITE_DONE as error. + */ +static bool +ExecuteCommand(sqlite3_stmt *stmt, Error &error) +{ + int result = ExecuteBusy(stmt); + if (result != SQLITE_DONE) { + SetError(error, stmt, result, "sqlite3_step() failed"); + return false; + } + + return true; +} + +/** + * Wrapper for ExecuteCommand() that returns the number of rows + * modified via sqlite3_changes(). Returns -1 on error. + */ +static inline int +ExecuteChanges(sqlite3_stmt *stmt, Error &error) +{ + if (!ExecuteCommand(stmt, error)) + return -1; + + return sqlite3_changes(sqlite3_db_handle(stmt)); +} + +/** + * Wrapper for ExecuteChanges() that returns true if at least one row + * was modified. Returns false if nothing was modified or if an error + * occurred. + */ +static inline bool +ExecuteModified(sqlite3_stmt *stmt, Error &error) +{ + return ExecuteChanges(stmt, error) > 0; +} + +template<typename F> +static inline bool +ExecuteForEach(sqlite3_stmt *stmt, Error &error, F &&f) +{ + while (true) { + int result = ExecuteBusy(stmt); + switch (result) { + case SQLITE_ROW: + f(); + break; + + case SQLITE_DONE: + return true; + + default: + SetError(error, stmt, result, "sqlite3_step() failed"); + return false; + } + } +} + +#endif diff --git a/src/lib/upnp/Action.hxx b/src/lib/upnp/Action.hxx index 28c88be92..bad398e1a 100644 --- a/src/lib/upnp/Action.hxx +++ b/src/lib/upnp/Action.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Callback.hxx b/src/lib/upnp/Callback.hxx index 85daf0a7e..4d86c0b53 100644 --- a/src/lib/upnp/Callback.hxx +++ b/src/lib/upnp/Callback.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/ClientInit.cxx b/src/lib/upnp/ClientInit.cxx index 77d9cf03d..50fcbdb16 100644 --- a/src/lib/upnp/ClientInit.cxx +++ b/src/lib/upnp/ClientInit.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/ClientInit.hxx b/src/lib/upnp/ClientInit.hxx index 645e64ca6..f49f255ee 100644 --- a/src/lib/upnp/ClientInit.hxx +++ b/src/lib/upnp/ClientInit.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/ContentDirectoryService.cxx b/src/lib/upnp/ContentDirectoryService.cxx index 0e5d2d955..0636505ab 100644 --- a/src/lib/upnp/ContentDirectoryService.cxx +++ b/src/lib/upnp/ContentDirectoryService.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/ContentDirectoryService.hxx b/src/lib/upnp/ContentDirectoryService.hxx index 0b03df2e7..bf6ab913a 100644 --- a/src/lib/upnp/ContentDirectoryService.hxx +++ b/src/lib/upnp/ContentDirectoryService.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -62,7 +62,8 @@ public: /** * Construct by copying data from device and service objects. * - * The discovery service does this: use getDirServices() + * The discovery service does this: use + * UPnPDeviceDirectory::GetDirectories() */ ContentDirectoryService(const UPnPDevice &device, const UPnPService &service); diff --git a/src/lib/upnp/Device.cxx b/src/lib/upnp/Device.cxx index 26bffd0f0..402a39166 100644 --- a/src/lib/upnp/Device.cxx +++ b/src/lib/upnp/Device.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Device.hxx b/src/lib/upnp/Device.hxx index dd7ecac2d..cdb065434 100644 --- a/src/lib/upnp/Device.hxx +++ b/src/lib/upnp/Device.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Discovery.cxx b/src/lib/upnp/Discovery.cxx index 1539e1512..f6a3ba122 100644 --- a/src/lib/upnp/Discovery.cxx +++ b/src/lib/upnp/Discovery.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -107,12 +107,12 @@ UPnPDeviceDirectory::LockRemove(const std::string &id) } inline void -UPnPDeviceDirectory::discoExplorer() +UPnPDeviceDirectory::Explore() { for (;;) { DiscoveredTask *tsk = 0; - if (!discoveredQueue.take(tsk)) { - discoveredQueue.workerExit(); + if (!queue.take(tsk)) { + queue.workerExit(); return; } @@ -128,7 +128,7 @@ UPnPDeviceDirectory::discoExplorer() } // Update or insert the device - ContentDirectoryDescriptor d(std::move(tsk->deviceId), + ContentDirectoryDescriptor d(std::move(tsk->device_id), MonotonicClockS(), tsk->expires); { @@ -148,10 +148,10 @@ UPnPDeviceDirectory::discoExplorer() } void * -UPnPDeviceDirectory::discoExplorer(void *ctx) +UPnPDeviceDirectory::Explore(void *ctx) { UPnPDeviceDirectory &directory = *(UPnPDeviceDirectory *)ctx; - directory.discoExplorer(); + directory.Explore(); return (void*)1; } @@ -161,7 +161,7 @@ UPnPDeviceDirectory::OnAlive(Upnp_Discovery *disco) if (isMSDevice(disco->DeviceType) || isCDService(disco->ServiceType)) { DiscoveredTask *tp = new DiscoveredTask(disco); - if (discoveredQueue.put(tp)) + if (queue.put(tp)) return UPNP_E_FINISH; } @@ -210,9 +210,8 @@ UPnPDeviceDirectory::Invoke(Upnp_EventType et, void *evp) } bool -UPnPDeviceDirectory::expireDevices(Error &error) +UPnPDeviceDirectory::ExpireDevices(Error &error) { - const ScopeLock protect(mutex); const unsigned now = MonotonicClockS(); bool didsomething = false; @@ -227,7 +226,7 @@ UPnPDeviceDirectory::expireDevices(Error &error) } if (didsomething) - return search(error); + return Search(error); return true; } @@ -236,8 +235,8 @@ UPnPDeviceDirectory::UPnPDeviceDirectory(UpnpClient_Handle _handle, UPnPDiscoveryListener *_listener) :handle(_handle), listener(_listener), - discoveredQueue("DiscoveredQueue"), - m_searchTimeout(2), m_lastSearch(0) + queue("DiscoveredQueue"), + search_timeout(2), last_search(0) { } @@ -249,24 +248,24 @@ UPnPDeviceDirectory::~UPnPDeviceDirectory() bool UPnPDeviceDirectory::Start(Error &error) { - if (!discoveredQueue.start(1, discoExplorer, this)) { + if (!queue.start(1, Explore, this)) { error.Set(upnp_domain, "Discover work queue start failed"); return false; } - return search(error); + return Search(error); } bool -UPnPDeviceDirectory::search(Error &error) +UPnPDeviceDirectory::Search(Error &error) { const unsigned now = MonotonicClockS(); - if (now - m_lastSearch < 10) + if (now - last_search < 10) return true; - m_lastSearch = now; + last_search = now; // We search both for device and service just in case. - int code = UpnpSearchAsync(handle, m_searchTimeout, + int code = UpnpSearchAsync(handle, search_timeout, ContentDirectorySType, GetUpnpCookie()); if (code != UPNP_E_SUCCESS) { error.Format(upnp_domain, code, @@ -275,7 +274,7 @@ UPnPDeviceDirectory::search(Error &error) return false; } - code = UpnpSearchAsync(handle, m_searchTimeout, + code = UpnpSearchAsync(handle, search_timeout, MediaServerDType, GetUpnpCookie()); if (code != UPNP_E_SUCCESS) { error.Format(upnp_domain, code, @@ -288,15 +287,14 @@ UPnPDeviceDirectory::search(Error &error) } bool -UPnPDeviceDirectory::getDirServices(std::vector<ContentDirectoryService> &out, +UPnPDeviceDirectory::GetDirectories(std::vector<ContentDirectoryService> &out, Error &error) { - // Has locking, do it before our own lock - if (!expireDevices(error)) - return false; - const ScopeLock protect(mutex); + if (!ExpireDevices(error)) + return false; + for (auto dit = directories.begin(); dit != directories.end(); dit++) { for (const auto &service : dit->device.services) { @@ -310,20 +308,19 @@ UPnPDeviceDirectory::getDirServices(std::vector<ContentDirectoryService> &out, } bool -UPnPDeviceDirectory::getServer(const char *friendlyName, +UPnPDeviceDirectory::GetServer(const char *friendly_name, ContentDirectoryService &server, Error &error) { - // Has locking, do it before our own lock - if (!expireDevices(error)) - return false; - const ScopeLock protect(mutex); + if (!ExpireDevices(error)) + return false; + for (const auto &i : directories) { const auto &device = i.device; - if (device.friendlyName != friendlyName) + if (device.friendlyName != friendly_name) continue; for (const auto &service : device.services) { diff --git a/src/lib/upnp/Discovery.hxx b/src/lib/upnp/Discovery.hxx index 767811840..1cf82b77e 100644 --- a/src/lib/upnp/Discovery.hxx +++ b/src/lib/upnp/Discovery.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -55,13 +55,13 @@ class UPnPDeviceDirectory final : UpnpCallback { */ struct DiscoveredTask { std::string url; - std::string deviceId; + std::string device_id; unsigned expires; // Seconds valid DiscoveredTask(const Upnp_Discovery *disco) :url(disco->Location), - deviceId(disco->DeviceId), - expires(disco->Expires) {} + device_id(disco->DeviceId), + expires(disco->Expires) {} }; /** @@ -97,19 +97,19 @@ class UPnPDeviceDirectory final : UpnpCallback { Mutex mutex; std::list<ContentDirectoryDescriptor> directories; - WorkQueue<DiscoveredTask *> discoveredQueue; + WorkQueue<DiscoveredTask *> queue; /** * The UPnP device search timeout, which should actually be * called delay because it's the base of a random delay that * the devices apply to avoid responding all at the same time. */ - int m_searchTimeout; + int search_timeout; /** * The MonotonicClockS() time stamp of the last search. */ - unsigned m_lastSearch; + unsigned last_search; public: UPnPDeviceDirectory(UpnpClient_Handle _handle, @@ -122,24 +122,26 @@ public: bool Start(Error &error); /** Retrieve the directory services currently seen on the network */ - bool getDirServices(std::vector<ContentDirectoryService> &, Error &); + bool GetDirectories(std::vector<ContentDirectoryService> &, Error &); /** * Get server by friendly name. */ - bool getServer(const char *friendlyName, + bool GetServer(const char *friendly_name, ContentDirectoryService &server, Error &error); private: - bool search(Error &error); + bool Search(Error &error); /** * Look at the devices and get rid of those which have not * been seen for too long. We do this when listing the top * directory. + * + * Caller must lock #mutex. */ - bool expireDevices(Error &error); + bool ExpireDevices(Error &error); void LockAdd(ContentDirectoryDescriptor &&d); void LockRemove(const std::string &id); @@ -149,12 +151,11 @@ private: * devices appearing and disappearing, and update the * directory pool accordingly. */ - static void *discoExplorer(void *); - void discoExplorer(); + static void *Explore(void *); + void Explore(); int OnAlive(Upnp_Discovery *disco); int OnByeBye(Upnp_Discovery *disco); - int cluCallBack(Upnp_EventType et, void *evp); /* virtual methods from class UpnpCallback */ virtual int Invoke(Upnp_EventType et, void *evp) override; diff --git a/src/lib/upnp/Domain.cxx b/src/lib/upnp/Domain.cxx index 010d4c7c2..d7700a067 100644 --- a/src/lib/upnp/Domain.cxx +++ b/src/lib/upnp/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Domain.hxx b/src/lib/upnp/Domain.hxx index ec01ef735..ff0cd9b85 100644 --- a/src/lib/upnp/Domain.hxx +++ b/src/lib/upnp/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Init.cxx b/src/lib/upnp/Init.cxx index 4fc606de9..1b471f53d 100644 --- a/src/lib/upnp/Init.cxx +++ b/src/lib/upnp/Init.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Init.hxx b/src/lib/upnp/Init.hxx index b23f8e2ab..796251862 100644 --- a/src/lib/upnp/Init.hxx +++ b/src/lib/upnp/Init.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Util.cxx b/src/lib/upnp/Util.cxx index 79cfb111c..912d993b4 100644 --- a/src/lib/upnp/Util.cxx +++ b/src/lib/upnp/Util.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/Util.hxx b/src/lib/upnp/Util.hxx index a59f23521..d3b0b049f 100644 --- a/src/lib/upnp/Util.hxx +++ b/src/lib/upnp/Util.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/upnp/WorkQueue.hxx b/src/lib/upnp/WorkQueue.hxx index fe8ce53f9..cd6b1161d 100644 --- a/src/lib/upnp/WorkQueue.hxx +++ b/src/lib/upnp/WorkQueue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -66,10 +66,7 @@ class WorkQueue { public: /** Create a WorkQueue - * @param name for message printing - * @param hi number of tasks on queue before clients blocks. Default 0 - * meaning no limit. hi == -1 means that the queue is disabled. - * @param lo minimum count of tasks before worker starts. Default 1. + * @param _name for message printing */ WorkQueue(const char *_name) :name(_name), @@ -86,7 +83,7 @@ public: /** Start the worker threads. * * @param nworkers number of threads copies to start. - * @param start_routine thread function. It should loop + * @param workproc thread function. It should loop * taking (QueueWorker::take()) and executing tasks. * @param arg initial parameter to thread function. * @return true if ok. diff --git a/src/lib/zlib/Domain.cxx b/src/lib/zlib/Domain.cxx index 96aad1350..b94076053 100644 --- a/src/lib/zlib/Domain.cxx +++ b/src/lib/zlib/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/lib/zlib/Domain.hxx b/src/lib/zlib/Domain.hxx index 653ac0209..9f0b9c5b0 100644 --- a/src/lib/zlib/Domain.hxx +++ b/src/lib/zlib/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/ls.cxx b/src/ls.cxx index 6ab68b6ab..41bd4ba97 100644 --- a/src/ls.cxx +++ b/src/ls.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,9 +19,9 @@ #include "config.h" #include "ls.hxx" +#include "client/Response.hxx" #include "util/StringUtil.hxx" #include "util/UriUtil.hxx" -#include "client/Client.hxx" #include <assert.h> @@ -30,7 +30,7 @@ * is detected at runtime and displayed as a urlhandler if the client is * connected by IPC socket. */ -static const char *remoteUrlPrefixes[] = { +static const char *const remoteUrlPrefixes[] = { #if defined(ENABLE_CURL) "http://", "https://", @@ -41,7 +41,7 @@ static const char *remoteUrlPrefixes[] = { "mmst://", "mmsu://", #endif -#ifdef HAVE_FFMPEG +#ifdef ENABLE_FFMPEG "gopher://", "rtp://", "rtsp://", @@ -58,7 +58,7 @@ static const char *remoteUrlPrefixes[] = { #ifdef ENABLE_CDIO_PARANOIA "cdda://", #endif -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA "alsa://", #endif NULL @@ -66,7 +66,7 @@ static const char *remoteUrlPrefixes[] = { void print_supported_uri_schemes_to_fp(FILE *fp) { - const char **prefixes = remoteUrlPrefixes; + const char *const*prefixes = remoteUrlPrefixes; #ifdef HAVE_UN fprintf(fp, " file://"); @@ -78,19 +78,20 @@ void print_supported_uri_schemes_to_fp(FILE *fp) fprintf(fp,"\n"); } -void print_supported_uri_schemes(Client &client) +void +print_supported_uri_schemes(Response &r) { - const char **prefixes = remoteUrlPrefixes; + const char *const *prefixes = remoteUrlPrefixes; while (*prefixes) { - client_printf(client, "handler: %s\n", *prefixes); + r.Format("handler: %s\n", *prefixes); prefixes++; } } bool uri_supported_scheme(const char *uri) { - const char **urlPrefixes = remoteUrlPrefixes; + const char *const*urlPrefixes = remoteUrlPrefixes; assert(uri_has_scheme(uri)); diff --git a/src/ls.hxx b/src/ls.hxx index f4b9be967..f34e3c6ab 100644 --- a/src/ls.hxx +++ b/src/ls.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,7 @@ #include <stdio.h> -class Client; +class Response; /** * Checks whether the scheme of the specified URI is supported by MPD. @@ -38,7 +38,7 @@ bool uri_supported_scheme(const char *url); * Send a list of supported URI schemes to the client. This is the * response to the "urlhandlers" command. */ -void print_supported_uri_schemes(Client &client); +void print_supported_uri_schemes(Response &r); /** * Send a list of supported URI schemes to a file pointer. diff --git a/src/mixer/Listener.hxx b/src/mixer/Listener.hxx index 6f48fbd4d..c65934dd8 100644 --- a/src/mixer/Listener.hxx +++ b/src/mixer/Listener.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/mixer/MixerAll.cxx b/src/mixer/MixerAll.cxx index 91891e870..835cf4eec 100644 --- a/src/mixer/MixerAll.cxx +++ b/src/mixer/MixerAll.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -146,7 +146,8 @@ MultipleOutputs::SetSoftwareVolume(unsigned volume) const auto mixer = ao->mixer; if (mixer != nullptr && - &mixer->plugin == &software_mixer_plugin) + (&mixer->plugin == &software_mixer_plugin || + &mixer->plugin == &null_mixer_plugin)) mixer_set_volume(mixer, volume, IgnoreError()); } } diff --git a/src/mixer/MixerControl.cxx b/src/mixer/MixerControl.cxx index 6d08140db..9de7809ec 100644 --- a/src/mixer/MixerControl.cxx +++ b/src/mixer/MixerControl.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,10 +28,10 @@ Mixer * mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &ao, MixerListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error) { - Mixer *mixer = plugin.init(event_loop, ao, listener, param, error); + Mixer *mixer = plugin.init(event_loop, ao, listener, block, error); assert(mixer == nullptr || mixer->IsPlugin(plugin)); diff --git a/src/mixer/MixerControl.hxx b/src/mixer/MixerControl.hxx index 75255d98c..700c720ba 100644 --- a/src/mixer/MixerControl.hxx +++ b/src/mixer/MixerControl.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ /** \file * - * Functions which manipulate a #mixer object. + * Functions which manipulate a #Mixer object. */ #ifndef MPD_MIXER_CONTROL_HXX @@ -31,12 +31,12 @@ class EventLoop; struct AudioOutput; struct MixerPlugin; class MixerListener; -struct config_param; +struct ConfigBlock; Mixer * mixer_new(EventLoop &event_loop, const MixerPlugin &plugin, AudioOutput &ao, MixerListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); void diff --git a/src/mixer/MixerInternal.hxx b/src/mixer/MixerInternal.hxx index 7b2cf2b32..02e06d85d 100644 --- a/src/mixer/MixerInternal.hxx +++ b/src/mixer/MixerInternal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/mixer/MixerList.hxx b/src/mixer/MixerList.hxx index e75b2e6ff..f2aa2e61c 100644 --- a/src/mixer/MixerList.hxx +++ b/src/mixer/MixerList.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,8 +27,10 @@ struct MixerPlugin; +extern const MixerPlugin null_mixer_plugin; extern const MixerPlugin software_mixer_plugin; extern const MixerPlugin alsa_mixer_plugin; +extern const MixerPlugin haiku_mixer_plugin; extern const MixerPlugin oss_mixer_plugin; extern const MixerPlugin roar_mixer_plugin; extern const MixerPlugin pulse_mixer_plugin; diff --git a/src/mixer/MixerPlugin.hxx b/src/mixer/MixerPlugin.hxx index 02bae844e..a40b4bb95 100644 --- a/src/mixer/MixerPlugin.hxx +++ b/src/mixer/MixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #ifndef MPD_MIXER_PLUGIN_HXX #define MPD_MIXER_PLUGIN_HXX -struct config_param; +struct ConfigBlock; struct AudioOutput; class Mixer; class MixerListener; @@ -46,7 +46,7 @@ struct MixerPlugin { */ Mixer *(*init)(EventLoop &event_loop, AudioOutput &ao, MixerListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); /** diff --git a/src/mixer/MixerType.cxx b/src/mixer/MixerType.cxx index cd45db0d9..23ca3f08a 100644 --- a/src/mixer/MixerType.cxx +++ b/src/mixer/MixerType.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,17 +23,19 @@ #include <assert.h> #include <string.h> -enum mixer_type +MixerType mixer_type_parse(const char *input) { assert(input != NULL); if (strcmp(input, "none") == 0 || strcmp(input, "disabled") == 0) - return MIXER_TYPE_NONE; + return MixerType::NONE; else if (strcmp(input, "hardware") == 0) - return MIXER_TYPE_HARDWARE; + return MixerType::HARDWARE; else if (strcmp(input, "software") == 0) - return MIXER_TYPE_SOFTWARE; + return MixerType::SOFTWARE; + else if (strcmp(input, "null") == 0) + return MixerType::NULL_; else - return MIXER_TYPE_UNKNOWN; + return MixerType::UNKNOWN; } diff --git a/src/mixer/MixerType.hxx b/src/mixer/MixerType.hxx index bfa2637b7..86037787f 100644 --- a/src/mixer/MixerType.hxx +++ b/src/mixer/MixerType.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,28 +20,31 @@ #ifndef MPD_MIXER_TYPE_HXX #define MPD_MIXER_TYPE_HXX -enum mixer_type { +enum class MixerType { /** parser error */ - MIXER_TYPE_UNKNOWN, + UNKNOWN, /** mixer disabled */ - MIXER_TYPE_NONE, + NONE, + + /** "null" mixer (virtual fake) */ + NULL_, /** software mixer with pcm_volume() */ - MIXER_TYPE_SOFTWARE, + SOFTWARE, /** hardware mixer (output's plugin) */ - MIXER_TYPE_HARDWARE, + HARDWARE, }; /** - * Parses a "mixer_type" setting from the configuration file. + * Parses a #MixerType setting from the configuration file. * - * @param input the configured string value; must not be NULL - * @return a #mixer_type value; MIXER_TYPE_UNKNOWN means #input could - * not be parsed + * @param input the configured string value; must not be NULL @return + * a #MixerType value; #MixerType::UNKNOWN means #input could not be + * parsed */ -enum mixer_type +MixerType mixer_type_parse(const char *input); #endif diff --git a/src/mixer/Volume.cxx b/src/mixer/Volume.cxx index abb01fb40..8bc8d879d 100644 --- a/src/mixer/Volume.cxx +++ b/src/mixer/Volume.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/mixer/Volume.hxx b/src/mixer/Volume.hxx index d787a6415..8c693bfc7 100644 --- a/src/mixer/Volume.hxx +++ b/src/mixer/Volume.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -49,6 +49,6 @@ save_sw_volume_state(BufferedOutputStream &os); */ gcc_pure unsigned -sw_volume_state_get_hash(void); +sw_volume_state_get_hash(); #endif diff --git a/src/mixer/plugins/AlsaMixerPlugin.cxx b/src/mixer/plugins/AlsaMixerPlugin.cxx index cd787182a..2b89f24e1 100644 --- a/src/mixer/plugins/AlsaMixerPlugin.cxx +++ b/src/mixer/plugins/AlsaMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -82,7 +82,7 @@ public: virtual ~AlsaMixer(); - void Configure(const config_param ¶m); + void Configure(const ConfigBlock &block); bool Setup(Error &error); /* virtual methods from class Mixer */ @@ -162,24 +162,24 @@ alsa_mixer_elem_callback(snd_mixer_elem_t *elem, unsigned mask) */ inline void -AlsaMixer::Configure(const config_param ¶m) +AlsaMixer::Configure(const ConfigBlock &block) { - device = param.GetBlockValue("mixer_device", + device = block.GetBlockValue("mixer_device", VOLUME_MIXER_ALSA_DEFAULT); - control = param.GetBlockValue("mixer_control", + control = block.GetBlockValue("mixer_control", VOLUME_MIXER_ALSA_CONTROL_DEFAULT); - index = param.GetBlockValue("mixer_index", + index = block.GetBlockValue("mixer_index", VOLUME_MIXER_ALSA_INDEX_DEFAULT); } static Mixer * alsa_mixer_init(EventLoop &event_loop, gcc_unused AudioOutput &ao, MixerListener &listener, - const config_param ¶m, + const ConfigBlock &block, gcc_unused Error &error) { AlsaMixer *am = new AlsaMixer(event_loop, listener); - am->Configure(param); + am->Configure(block); return am; } diff --git a/src/mixer/plugins/HaikuMixerPlugin.cxx b/src/mixer/plugins/HaikuMixerPlugin.cxx new file mode 100644 index 000000000..d6dfb663d --- /dev/null +++ b/src/mixer/plugins/HaikuMixerPlugin.cxx @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003-2015 The Music Player Daemon Project + * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft + * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen + * Copyright (C) 2014-2015 François 'mmu_man' Revol + * + * 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 "mixer/MixerInternal.hxx" +#include "output/plugins/HaikuOutputPlugin.hxx" +#include "Compiler.h" + +class HaikuMixer final : public Mixer { + /** the base mixer class */ + HaikuOutput &self; + +public: + HaikuMixer(HaikuOutput &_output, MixerListener &_listener) + :Mixer(haiku_mixer_plugin, _listener), + self(_output) {} + + /* virtual methods from class Mixer */ + virtual bool Open(gcc_unused Error &error) override { + return true; + } + + virtual void Close() override { + } + + virtual int GetVolume(Error &error) override; + virtual bool SetVolume(unsigned volume, Error &error) override; +}; + +static Mixer * +haiku_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao, + MixerListener &listener, + gcc_unused const ConfigBlock &block, + gcc_unused Error &error) +{ + return new HaikuMixer((HaikuOutput &)ao, listener); +} + +int +HaikuMixer::GetVolume(gcc_unused Error &error) +{ + return haiku_output_get_volume(self); +} + +bool +HaikuMixer::SetVolume(unsigned volume, gcc_unused Error &error) +{ + return haiku_output_set_volume(self, volume); +} + +const MixerPlugin haiku_mixer_plugin = { + haiku_mixer_init, + false, +}; diff --git a/src/mixer/plugins/NullMixerPlugin.cxx b/src/mixer/plugins/NullMixerPlugin.cxx new file mode 100644 index 000000000..d846a6be1 --- /dev/null +++ b/src/mixer/plugins/NullMixerPlugin.cxx @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2003-2015 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 "mixer/MixerInternal.hxx" + +class NullMixer final : public Mixer { + /** + * The current volume in percent (0..100). + */ + unsigned volume; + +public: + NullMixer(MixerListener &_listener) + :Mixer(null_mixer_plugin, _listener), + volume(100) + { + } + + /* virtual methods from class Mixer */ + bool Open(gcc_unused Error &error) override { + return true; + } + + void Close() override { + } + + int GetVolume(gcc_unused Error &error) override { + return volume; + } + + bool SetVolume(unsigned _volume, gcc_unused Error &error) override { + volume = _volume; + return true; + } +}; + +static Mixer * +null_mixer_init(gcc_unused EventLoop &event_loop, + gcc_unused AudioOutput &ao, + MixerListener &listener, + gcc_unused const ConfigBlock &block, + gcc_unused Error &error) +{ + return new NullMixer(listener); +} + +const MixerPlugin null_mixer_plugin = { + null_mixer_init, + true, +}; diff --git a/src/mixer/plugins/OssMixerPlugin.cxx b/src/mixer/plugins/OssMixerPlugin.cxx index 6615c7022..ae198492c 100644 --- a/src/mixer/plugins/OssMixerPlugin.cxx +++ b/src/mixer/plugins/OssMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "mixer/MixerInternal.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "system/fd_util.h" #include "util/ASCII.hxx" #include "util/Error.hxx" @@ -52,7 +52,7 @@ public: OssMixer(MixerListener &_listener) :Mixer(oss_mixer_plugin, _listener) {} - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); /* virtual methods from class Mixer */ virtual bool Open(Error &error) override; @@ -79,10 +79,10 @@ oss_find_mixer(const char *name) } inline bool -OssMixer::Configure(const config_param ¶m, Error &error) +OssMixer::Configure(const ConfigBlock &block, Error &error) { - device = param.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT); - control = param.GetBlockValue("mixer_control"); + device = block.GetBlockValue("mixer_device", VOLUME_MIXER_OSS_DEFAULT); + control = block.GetBlockValue("mixer_control"); if (control != NULL) { volume_control = oss_find_mixer(control); @@ -100,12 +100,12 @@ OssMixer::Configure(const config_param ¶m, Error &error) static Mixer * oss_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused AudioOutput &ao, MixerListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error) { OssMixer *om = new OssMixer(listener); - if (!om->Configure(param, error)) { + if (!om->Configure(block, error)) { delete om; return nullptr; } diff --git a/src/mixer/plugins/PulseMixerPlugin.cxx b/src/mixer/plugins/PulseMixerPlugin.cxx index c5f20723b..f2b17a75a 100644 --- a/src/mixer/plugins/PulseMixerPlugin.cxx +++ b/src/mixer/plugins/PulseMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,18 +19,18 @@ #include "config.h" #include "PulseMixerPlugin.hxx" +#include "lib/pulse/Domain.hxx" +#include "lib/pulse/LogError.hxx" #include "mixer/MixerInternal.hxx" #include "mixer/Listener.hxx" #include "output/plugins/PulseOutputPlugin.hxx" #include "util/Error.hxx" -#include "util/Domain.hxx" #include "Log.hxx" #include <pulse/context.h> #include <pulse/introspect.h> #include <pulse/stream.h> #include <pulse/subscribe.h> -#include <pulse/error.h> #include <assert.h> @@ -55,19 +55,17 @@ public: int GetVolumeInternal(Error &error); /* virtual methods from class Mixer */ - virtual bool Open(gcc_unused Error &error) override { + bool Open(gcc_unused Error &error) override { return true; } - virtual void Close() override { + void Close() override { } - virtual int GetVolume(Error &error) override; - virtual bool SetVolume(unsigned volume, Error &error) override; + int GetVolume(Error &error) override; + bool SetVolume(unsigned volume, Error &error) override; }; -static constexpr Domain pulse_mixer_domain("pulse_mixer"); - void PulseMixer::Offline() { @@ -120,9 +118,8 @@ PulseMixer::Update(pa_context *context, pa_stream *stream) pa_stream_get_index(stream), pulse_mixer_volume_cb, this); if (o == nullptr) { - FormatError(pulse_mixer_domain, - "pa_context_get_sink_input_info() failed: %s", - pa_strerror(pa_context_errno(context))); + LogPulseError(context, + "pa_context_get_sink_input_info() failed"); Offline(); return; } @@ -142,9 +139,8 @@ pulse_mixer_on_connect(gcc_unused PulseMixer &pm, (pa_subscription_mask_t)PA_SUBSCRIPTION_MASK_SINK_INPUT, nullptr, nullptr); if (o == nullptr) { - FormatError(pulse_mixer_domain, - "pa_context_subscribe() failed: %s", - pa_strerror(pa_context_errno(context))); + LogPulseError(context, + "pa_context_subscribe() failed"); return; } @@ -167,7 +163,7 @@ pulse_mixer_on_change(PulseMixer &pm, static Mixer * pulse_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao, MixerListener &listener, - gcc_unused const config_param ¶m, + gcc_unused const ConfigBlock &block, gcc_unused Error &error) { PulseOutput &po = (PulseOutput &)ao; @@ -212,7 +208,7 @@ PulseMixer::SetVolume(unsigned new_volume, Error &error) if (!online) { pulse_output_unlock(output); - error.Set(pulse_mixer_domain, "disconnected"); + error.Set(pulse_domain, "disconnected"); return false; } diff --git a/src/mixer/plugins/PulseMixerPlugin.hxx b/src/mixer/plugins/PulseMixerPlugin.hxx index 9b3a6daf1..64605ea8d 100644 --- a/src/mixer/plugins/PulseMixerPlugin.hxx +++ b/src/mixer/plugins/PulseMixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/mixer/plugins/RoarMixerPlugin.cxx b/src/mixer/plugins/RoarMixerPlugin.cxx index 8e198478d..9123762f2 100644 --- a/src/mixer/plugins/RoarMixerPlugin.cxx +++ b/src/mixer/plugins/RoarMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen * @@ -48,7 +48,7 @@ public: static Mixer * roar_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao, MixerListener &listener, - gcc_unused const config_param ¶m, + gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new RoarMixer((RoarOutput &)ao, listener); diff --git a/src/mixer/plugins/SoftwareMixerPlugin.cxx b/src/mixer/plugins/SoftwareMixerPlugin.cxx index f14766002..d35e7f469 100644 --- a/src/mixer/plugins/SoftwareMixerPlugin.cxx +++ b/src/mixer/plugins/SoftwareMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ #include "filter/FilterInternal.hxx" #include "filter/plugins/VolumeFilterPlugin.hxx" #include "pcm/Volume.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "util/Error.hxx" #include <assert.h> @@ -34,7 +34,7 @@ static Filter * CreateVolumeFilter() { - return filter_new(&volume_filter_plugin, config_param(), + return filter_new(&volume_filter_plugin, ConfigBlock(), IgnoreError()); } @@ -90,7 +90,7 @@ static Mixer * software_mixer_init(gcc_unused EventLoop &event_loop, gcc_unused AudioOutput &ao, MixerListener &listener, - gcc_unused const config_param ¶m, + gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new SoftwareMixer(listener); diff --git a/src/mixer/plugins/SoftwareMixerPlugin.hxx b/src/mixer/plugins/SoftwareMixerPlugin.hxx index 581d2ac17..f9be1d9d9 100644 --- a/src/mixer/plugins/SoftwareMixerPlugin.hxx +++ b/src/mixer/plugins/SoftwareMixerPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/mixer/plugins/WinmmMixerPlugin.cxx b/src/mixer/plugins/WinmmMixerPlugin.cxx index e0436011a..51d8092f9 100644 --- a/src/mixer/plugins/WinmmMixerPlugin.cxx +++ b/src/mixer/plugins/WinmmMixerPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -69,7 +69,7 @@ winmm_volume_encode(int volume) static Mixer * winmm_mixer_init(gcc_unused EventLoop &event_loop, AudioOutput &ao, MixerListener &listener, - gcc_unused const config_param ¶m, + gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new WinmmMixer((WinmmOutput &)ao, listener); diff --git a/src/neighbor/Explorer.hxx b/src/neighbor/Explorer.hxx index 84a54840c..abf426cd9 100644 --- a/src/neighbor/Explorer.hxx +++ b/src/neighbor/Explorer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/neighbor/Glue.cxx b/src/neighbor/Glue.cxx index fbf25cc8d..4d5d7b4b0 100644 --- a/src/neighbor/Glue.cxx +++ b/src/neighbor/Glue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,8 +24,8 @@ #include "NeighborPlugin.hxx" #include "Info.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" #include "config/ConfigError.hxx" +#include "config/Block.hxx" #include "util/Error.hxx" NeighborGlue::Explorer::~Explorer() @@ -37,9 +37,9 @@ NeighborGlue::~NeighborGlue() {} static NeighborExplorer * CreateNeighborExplorer(EventLoop &loop, NeighborListener &listener, - const config_param ¶m, Error &error) + const ConfigBlock &block, Error &error) { - const char *plugin_name = param.GetBlockValue("plugin"); + const char *plugin_name = block.GetBlockValue("plugin"); if (plugin_name == nullptr) { error.Set(config_domain, "Missing \"plugin\" configuration"); @@ -53,18 +53,18 @@ CreateNeighborExplorer(EventLoop &loop, NeighborListener &listener, return nullptr; } - return plugin->create(loop, listener, param, error); + return plugin->create(loop, listener, block, error); } bool NeighborGlue::Init(EventLoop &loop, NeighborListener &listener, Error &error) { - for (const config_param *param = config_get_param(CONF_NEIGHBORS); - param != nullptr; param = param->next) { + for (const auto *block = config_get_block(ConfigBlockOption::NEIGHBORS); + block != nullptr; block = block->next) { NeighborExplorer *explorer = - CreateNeighborExplorer(loop, listener, *param, error); + CreateNeighborExplorer(loop, listener, *block, error); if (explorer == nullptr) { - error.FormatPrefix("Line %i: ", param->line); + error.FormatPrefix("Line %i: ", block->line); return false; } diff --git a/src/neighbor/Glue.hxx b/src/neighbor/Glue.hxx index 92c612d22..fc1778d05 100644 --- a/src/neighbor/Glue.hxx +++ b/src/neighbor/Glue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/neighbor/Info.hxx b/src/neighbor/Info.hxx index ac4806f14..a9e629213 100644 --- a/src/neighbor/Info.hxx +++ b/src/neighbor/Info.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/neighbor/Listener.hxx b/src/neighbor/Listener.hxx index 20295f5a9..dcf6cafff 100644 --- a/src/neighbor/Listener.hxx +++ b/src/neighbor/Listener.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/neighbor/NeighborPlugin.hxx b/src/neighbor/NeighborPlugin.hxx index 0d4ebaa7b..116f47c50 100644 --- a/src/neighbor/NeighborPlugin.hxx +++ b/src/neighbor/NeighborPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #ifndef MPD_NEIGHBOR_PLUGIN_HXX #define MPD_NEIGHBOR_PLUGIN_HXX -struct config_param; +struct ConfigBlock; class Error; class EventLoop; class NeighborListener; @@ -33,7 +33,7 @@ struct NeighborPlugin { * Allocates and configures a #NeighborExplorer instance. */ NeighborExplorer *(*create)(EventLoop &loop, NeighborListener &listener, - const config_param ¶m, + const ConfigBlock &block, Error &error); }; diff --git a/src/neighbor/Registry.cxx b/src/neighbor/Registry.cxx index f6d1f97b3..d58e3b974 100644 --- a/src/neighbor/Registry.cxx +++ b/src/neighbor/Registry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,7 +29,7 @@ const NeighborPlugin *const neighbor_plugins[] = { #ifdef ENABLE_SMBCLIENT &smbclient_neighbor_plugin, #endif -#ifdef HAVE_LIBUPNP +#ifdef ENABLE_UPNP &upnp_neighbor_plugin, #endif nullptr diff --git a/src/neighbor/Registry.hxx b/src/neighbor/Registry.hxx index 0b89e537d..59c9f1fb2 100644 --- a/src/neighbor/Registry.hxx +++ b/src/neighbor/Registry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/neighbor/plugins/SmbclientNeighborPlugin.cxx b/src/neighbor/plugins/SmbclientNeighborPlugin.cxx index 2701b0ccd..d4c73f9a8 100644 --- a/src/neighbor/plugins/SmbclientNeighborPlugin.cxx +++ b/src/neighbor/plugins/SmbclientNeighborPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -216,7 +216,7 @@ SmbclientNeighborExplorer::Run() prev = i; } else { /* can't see it anymore: move to "lost" */ -#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#if CLANG_OR_GCC_VERSION(4,7) lost.splice_after(lost.before_begin(), list, prev); #else /* the forward_list::splice_after() lvalue @@ -273,7 +273,7 @@ SmbclientNeighborExplorer::ThreadFunc(void *ctx) static NeighborExplorer * smbclient_neighbor_create(gcc_unused EventLoop &loop, NeighborListener &listener, - gcc_unused const config_param ¶m, + gcc_unused const ConfigBlock &block, gcc_unused Error &error) { if (!SmbclientInit(error)) diff --git a/src/neighbor/plugins/SmbclientNeighborPlugin.hxx b/src/neighbor/plugins/SmbclientNeighborPlugin.hxx index 12ec9c0cd..be5560eee 100644 --- a/src/neighbor/plugins/SmbclientNeighborPlugin.hxx +++ b/src/neighbor/plugins/SmbclientNeighborPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/neighbor/plugins/UpnpNeighborPlugin.cxx b/src/neighbor/plugins/UpnpNeighborPlugin.cxx index 253e4c13b..3c0cb8843 100644 --- a/src/neighbor/plugins/UpnpNeighborPlugin.cxx +++ b/src/neighbor/plugins/UpnpNeighborPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -100,7 +100,7 @@ UpnpNeighborExplorer::GetList() const { Error error; - if (!discovery->getDirServices(tmp, error)) + if (!discovery->GetDirectories(tmp, error)) LogError(error); } @@ -127,7 +127,7 @@ UpnpNeighborExplorer::LostUPnP(const ContentDirectoryService &service) static NeighborExplorer * upnp_neighbor_create(gcc_unused EventLoop &loop, NeighborListener &listener, - gcc_unused const config_param ¶m, + gcc_unused const ConfigBlock &block, gcc_unused Error &error) { return new UpnpNeighborExplorer(listener); diff --git a/src/neighbor/plugins/UpnpNeighborPlugin.hxx b/src/neighbor/plugins/UpnpNeighborPlugin.hxx index 78e4ccf13..abda3addd 100644 --- a/src/neighbor/plugins/UpnpNeighborPlugin.hxx +++ b/src/neighbor/plugins/UpnpNeighborPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/net/AllocatedSocketAddress.cxx b/src/net/AllocatedSocketAddress.cxx new file mode 100644 index 000000000..61615a72b --- /dev/null +++ b/src/net/AllocatedSocketAddress.cxx @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "AllocatedSocketAddress.hxx" + +#include <assert.h> +#include <string.h> + +#ifdef HAVE_UN +#include <sys/un.h> +#endif + +AllocatedSocketAddress & +AllocatedSocketAddress::operator=(SocketAddress src) +{ + if (src.IsNull()) { + Clear(); + } else { + SetSize(src.GetSize()); + memcpy(address, src.GetAddress(), size); + } + + return *this; +} + +void +AllocatedSocketAddress::SetSize(size_type new_size) +{ + if (size == new_size) + return; + + free(address); + size = new_size; + address = (struct sockaddr *)malloc(size); +} + +#ifdef HAVE_UN + +void +AllocatedSocketAddress::SetLocal(const char *path) +{ + const bool is_abstract = *path == '@'; + + /* sun_path must be null-terminated unless it's an abstract + socket */ + const size_t path_length = strlen(path) + !is_abstract; + + struct sockaddr_un *sun; + SetSize(sizeof(*sun) - sizeof(sun->sun_path) + path_length); + sun = (struct sockaddr_un *)address; + sun->sun_family = AF_UNIX; + memcpy(sun->sun_path, path, path_length); + + if (is_abstract) + sun->sun_path[0] = 0; +} + +#endif diff --git a/src/net/AllocatedSocketAddress.hxx b/src/net/AllocatedSocketAddress.hxx new file mode 100644 index 000000000..db02488df --- /dev/null +++ b/src/net/AllocatedSocketAddress.hxx @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALLOCATED_SOCKET_ADDRESS_HPP +#define ALLOCATED_SOCKET_ADDRESS_HPP + +#include "SocketAddress.hxx" +#include "Features.hxx" +#include "Compiler.h" + +#include <algorithm> + +#include <stdlib.h> + +struct sockaddr; +class Error; + +class AllocatedSocketAddress { +public: + typedef SocketAddress::size_type size_type; + +private: + struct sockaddr *address; + size_type size; + + AllocatedSocketAddress(struct sockaddr *_address, + size_type _size) + :address(_address), size(_size) {} + +public: + AllocatedSocketAddress():address(nullptr), size(0) {} + + explicit AllocatedSocketAddress(SocketAddress src) + :address(nullptr), size(0) { + *this = src; + } + + AllocatedSocketAddress(const AllocatedSocketAddress &) = delete; + + AllocatedSocketAddress(AllocatedSocketAddress &&src) + :address(src.address), size(src.size) { + src.address = nullptr; + src.size = 0; + } + + ~AllocatedSocketAddress() { + free(address); + } + + AllocatedSocketAddress &operator=(SocketAddress src); + + AllocatedSocketAddress &operator=(const AllocatedSocketAddress &) = delete; + + AllocatedSocketAddress &operator=(AllocatedSocketAddress &&src) { + std::swap(address, src.address); + std::swap(size, src.size); + return *this; + } + + gcc_pure + bool operator==(SocketAddress other) const { + return (SocketAddress)*this == other; + } + + bool operator!=(SocketAddress &other) const { + return !(*this == other); + } + + gcc_const + static AllocatedSocketAddress Null() { + return AllocatedSocketAddress(nullptr, 0); + } + + bool IsNull() const { + return address == nullptr; + } + + size_type GetSize() const { + return size; + } + + const struct sockaddr *GetAddress() const { + return address; + } + + operator SocketAddress() const { + return SocketAddress(address, size); + } + + operator const struct sockaddr *() const { + return address; + } + + int GetFamily() const { + return address->sa_family; + } + + /** + * Does the object have a well-defined address? Check !IsNull() + * before calling this method. + */ + bool IsDefined() const { + return GetFamily() != AF_UNSPEC; + } + + void Clear() { + free(address); + address = nullptr; + size = 0; + } + +#ifdef HAVE_UN + /** + * Make this a "local" address (UNIX domain socket). If the path + * begins with a '@', then the rest specifies an "abstract" local + * address. + */ + void SetLocal(const char *path); +#endif + +private: + void SetSize(size_type new_size); +}; + +#endif diff --git a/src/net/Features.hxx b/src/net/Features.hxx new file mode 100644 index 000000000..05dcc5659 --- /dev/null +++ b/src/net/Features.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2015 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 NET_FEATURES_HXX +#define NET_FEATURES_HXX + +/* feature macros are defined in config.h, and this header verifies + that it has been included earlier */ +#include "check.h" + +#endif diff --git a/src/system/Resolver.cxx b/src/net/Resolver.cxx index a94217bac..189c5bc9c 100644 --- a/src/system/Resolver.cxx +++ b/src/net/Resolver.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,22 +19,16 @@ #include "config.h" #include "Resolver.hxx" +#include "SocketAddress.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#ifndef WIN32 -#include <sys/socket.h> -#include <netdb.h> -#ifdef HAVE_TCP -#include <netinet/in.h> -#endif -#else -#include <ws2tcpip.h> -#include <winsock.h> -#endif +#include <string> -#ifdef HAVE_UN -#include <sys/un.h> +#ifdef WIN32 +#include <ws2tcpip.h> +#else +#include <netdb.h> #endif #include <string.h> @@ -42,64 +36,6 @@ const Domain resolver_domain("resolver"); -std::string -sockaddr_to_string(const struct sockaddr *sa, size_t length) -{ -#ifdef HAVE_UN - if (sa->sa_family == AF_UNIX) { - /* return path of UNIX domain sockets */ - const sockaddr_un &s_un = *(const sockaddr_un *)sa; - if (length < sizeof(s_un) || s_un.sun_path[0] == 0) - return "local"; - - return s_un.sun_path; - } -#endif - -#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) - const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)sa; - struct sockaddr_in a4; -#endif - int ret; - char host[NI_MAXHOST], serv[NI_MAXSERV]; - -#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) - if (sa->sa_family == AF_INET6 && - IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) { - /* convert "::ffff:127.0.0.1" to "127.0.0.1" */ - - memset(&a4, 0, sizeof(a4)); - a4.sin_family = AF_INET; - memcpy(&a4.sin_addr, ((const char *)&a6->sin6_addr) + 12, - sizeof(a4.sin_addr)); - a4.sin_port = a6->sin6_port; - - sa = (const struct sockaddr *)&a4; - length = sizeof(a4); - } -#endif - - ret = getnameinfo(sa, length, host, sizeof(host), serv, sizeof(serv), - NI_NUMERICHOST|NI_NUMERICSERV); - if (ret != 0) - return "unknown"; - -#ifdef HAVE_IPV6 - if (strchr(host, ':') != nullptr) { - std::string result("["); - result.append(host); - result.append("]:"); - result.append(serv); - return result; - } -#endif - - std::string result(host); - result.push_back(':'); - result.append(serv); - return result; -} - struct addrinfo * resolve_host_port(const char *host_port, unsigned default_port, int flags, int socktype, diff --git a/src/system/Resolver.hxx b/src/net/Resolver.hxx index 54922d98f..826cc4787 100644 --- a/src/system/Resolver.hxx +++ b/src/net/Resolver.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,13 +20,9 @@ #ifndef MPD_RESOLVER_HXX #define MPD_RESOLVER_HXX +#include "check.h" #include "Compiler.h" -#include <string> - -#include <stddef.h> - -struct sockaddr; struct addrinfo; class Error; class Domain; @@ -34,17 +30,6 @@ class Domain; extern const Domain resolver_domain; /** - * Converts the specified socket address into a string in the form - * "IP:PORT". - * - * @param sa the sockaddr struct - * @param length the length of #sa in bytes - */ -gcc_pure -std::string -sockaddr_to_string(const sockaddr *sa, size_t length); - -/** * Resolve a specification in the form "host", "host:port", * "[host]:port". This is a convenience wrapper for getaddrinfo(). * diff --git a/src/net/SocketAddress.cxx b/src/net/SocketAddress.cxx new file mode 100644 index 000000000..38aeb8d6d --- /dev/null +++ b/src/net/SocketAddress.cxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "SocketAddress.hxx" + +#include <string.h> + +bool +SocketAddress::operator==(SocketAddress other) const +{ + return size == other.size && memcmp(address, other.address, size) == 0; +} diff --git a/src/net/SocketAddress.hxx b/src/net/SocketAddress.hxx new file mode 100644 index 000000000..0577edd72 --- /dev/null +++ b/src/net/SocketAddress.hxx @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SOCKET_ADDRESS_HXX +#define SOCKET_ADDRESS_HXX + +#include "Compiler.h" + +#include <cstddef> + +#ifdef WIN32 +#include <winsock2.h> +#else +#include <sys/socket.h> +#endif + +/** + * An OO wrapper for struct sockaddr. + */ +class SocketAddress { +public: +#ifdef WIN32 + typedef int size_type; +#else + typedef socklen_t size_type; +#endif + +private: + const struct sockaddr *address; + size_type size; + +public: + SocketAddress() = default; + + constexpr SocketAddress(std::nullptr_t):address(nullptr), size(0) {} + + constexpr SocketAddress(const struct sockaddr *_address, + size_type _size) + :address(_address), size(_size) {} + + static constexpr SocketAddress Null() { + return nullptr; + } + + constexpr bool IsNull() const { + return address == nullptr; + } + + const struct sockaddr *GetAddress() const { + return address; + } + + constexpr size_type GetSize() const { + return size; + } + + constexpr int GetFamily() const { + return address->sa_family; + } + + /** + * Does the object have a well-defined address? Check !IsNull() + * before calling this method. + */ + bool IsDefined() const { + return GetFamily() != AF_UNSPEC; + } + + gcc_pure + bool operator==(const SocketAddress other) const; + + bool operator!=(const SocketAddress other) const { + return !(*this == other); + } +}; + +#endif diff --git a/src/system/SocketError.cxx b/src/net/SocketError.cxx index e138f4dd3..efa969b1f 100644 --- a/src/system/SocketError.cxx +++ b/src/net/SocketError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "SocketError.hxx" #include "util/Domain.hxx" +#include "util/Macros.hxx" #include <string.h> @@ -29,13 +30,31 @@ const Domain socket_domain("socket"); SocketErrorMessage::SocketErrorMessage(socket_error_t code) { +#ifdef _UNICODE + wchar_t buffer[ARRAY_SIZE(msg)]; +#else + auto *buffer = msg; +#endif + DWORD nbytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, code, 0, - (LPSTR)msg, sizeof(msg), NULL); - if (nbytes == 0) + nullptr, code, 0, + buffer, ARRAY_SIZE(msg), nullptr); + if (nbytes == 0) { strcpy(msg, "Unknown error"); + return; + } + +#ifdef _UNICODE + auto length = WideCharToMultiByte(CP_UTF8, 0, buffer, -1, + msg, ARRAY_SIZE(msg), + nullptr, nullptr); + if (length <= 0) { + strcpy(msg, "WideCharToMultiByte() error"); + return; + } +#endif } #else diff --git a/src/system/SocketError.hxx b/src/net/SocketError.hxx index 01abc9884..c9435d6fb 100644 --- a/src/system/SocketError.hxx +++ b/src/net/SocketError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/SocketUtil.cxx b/src/net/SocketUtil.cxx index b9df0d59d..72d710862 100644 --- a/src/system/SocketUtil.cxx +++ b/src/net/SocketUtil.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,25 +19,19 @@ #include "config.h" #include "SocketUtil.hxx" +#include "SocketAddress.hxx" #include "SocketError.hxx" -#include "fd_util.h" +#include "system/fd_util.h" #include <unistd.h> -#ifndef WIN32 -#include <sys/socket.h> -#else -#include <ws2tcpip.h> -#include <winsock.h> -#endif - #ifdef HAVE_IPV6 #include <string.h> #endif int socket_bind_listen(int domain, int type, int protocol, - const struct sockaddr *address, size_t address_length, + SocketAddress address, int backlog, Error &error) { @@ -60,7 +54,7 @@ socket_bind_listen(int domain, int type, int protocol, return -1; } - ret = bind(fd, address, address_length); + ret = bind(fd, address.GetAddress(), address.GetSize()); if (ret < 0) { SetSocketError(error); close_socket(fd); @@ -75,7 +69,7 @@ socket_bind_listen(int domain, int type, int protocol, return -1; } -#ifdef HAVE_STRUCT_UCRED +#if defined(HAVE_STRUCT_UCRED) && defined(SO_PASSCRED) setsockopt(fd, SOL_SOCKET, SO_PASSCRED, (const char *) &reuse, sizeof(reuse)); #endif diff --git a/src/system/SocketUtil.hxx b/src/net/SocketUtil.hxx index 652788759..e7f6b8eaa 100644 --- a/src/system/SocketUtil.hxx +++ b/src/net/SocketUtil.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,9 +26,7 @@ #ifndef MPD_SOCKET_UTIL_HXX #define MPD_SOCKET_UTIL_HXX -#include <stddef.h> - -struct sockaddr; +class SocketAddress; class Error; /** @@ -39,7 +37,6 @@ class Error; * @param type the socket type, e.g. SOCK_STREAM * @param protocol the protocol, usually 0 to let the kernel choose * @param address the address to listen on - * @param address_length the size of #address * @param backlog the backlog parameter for the listen() system call * @param error location to store the error occurring, or NULL to * ignore errors @@ -47,7 +44,7 @@ class Error; */ int socket_bind_listen(int domain, int type, int protocol, - const struct sockaddr *address, size_t address_length, + SocketAddress address, int backlog, Error &error); diff --git a/src/net/StaticSocketAddress.cxx b/src/net/StaticSocketAddress.cxx new file mode 100644 index 000000000..94db4a49c --- /dev/null +++ b/src/net/StaticSocketAddress.cxx @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StaticSocketAddress.hxx" + +#include <algorithm> + +#include <string.h> + +StaticSocketAddress & +StaticSocketAddress::operator=(SocketAddress other) +{ + size = std::min(other.GetSize(), GetCapacity()); + memcpy(&address, other.GetAddress(), size); + return *this; +} diff --git a/src/net/StaticSocketAddress.hxx b/src/net/StaticSocketAddress.hxx new file mode 100644 index 000000000..c8cef9dfe --- /dev/null +++ b/src/net/StaticSocketAddress.hxx @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef STATIC_SOCKET_ADDRESS_HXX +#define STATIC_SOCKET_ADDRESS_HXX + +#include "SocketAddress.hxx" +#include "Compiler.h" + +#include <assert.h> + +/** + * An OO wrapper for struct sockaddr_storage. + */ +class StaticSocketAddress { +public: + typedef SocketAddress::size_type size_type; + +private: + size_type size; + struct sockaddr_storage address; + +public: + StaticSocketAddress() = default; + + StaticSocketAddress &operator=(SocketAddress other); + + operator SocketAddress() const { + return SocketAddress(reinterpret_cast<const struct sockaddr *>(&address), + size); + } + + struct sockaddr *GetAddress() { + return reinterpret_cast<struct sockaddr *>(&address); + } + + const struct sockaddr *GetAddress() const { + return reinterpret_cast<const struct sockaddr *>(&address); + } + + constexpr size_type GetCapacity() const { + return sizeof(address); + } + + size_type GetSize() const { + return size; + } + + void SetSize(size_type _size) { + assert(_size > 0); + assert(size_t(_size) <= sizeof(address)); + + size = _size; + } + + int GetFamily() const { + return address.ss_family; + } + + bool IsDefined() const { + return GetFamily() != AF_UNSPEC; + } + + void Clear() { + address.ss_family = AF_UNSPEC; + } + + gcc_pure + bool operator==(SocketAddress other) const { + return (SocketAddress)*this == other; + } + + bool operator!=(SocketAddress &other) const { + return !(*this == other); + } +}; + +#endif diff --git a/src/net/ToString.cxx b/src/net/ToString.cxx new file mode 100644 index 000000000..1acf0320b --- /dev/null +++ b/src/net/ToString.cxx @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2011-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ToString.hxx" +#include "Features.hxx" +#include "SocketAddress.hxx" + +#include <algorithm> + +#ifdef WIN32 +#include <ws2tcpip.h> +#else +#include <netdb.h> +#ifdef HAVE_TCP +#include <netinet/in.h> +#endif +#endif + +#ifdef HAVE_UN +#include <sys/un.h> +#endif + +#include <assert.h> +#include <string.h> + +#ifdef HAVE_UN + +static std::string +LocalAddressToString(const struct sockaddr_un &s_un, size_t size) +{ + const size_t prefix_size = (size_t) + ((struct sockaddr_un *)nullptr)->sun_path; + assert(size >= prefix_size); + + size_t result_length = size - prefix_size; + + /* remove the trailing null terminator */ + if (result_length > 0 && s_un.sun_path[result_length - 1] == 0) + --result_length; + + if (result_length == 0) + return "local"; + + std::string result(s_un.sun_path, result_length); + + /* replace all null bytes with '@'; this also handles abstract + addresses (Linux specific) */ + std::replace(result.begin(), result.end(), '\0', '@'); + + return result; +} + +#endif + +#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) + +gcc_pure +static bool +IsV4Mapped(SocketAddress address) +{ + if (address.GetFamily() != AF_INET6) + return false; + + const auto &a6 = *(const struct sockaddr_in6 *)address.GetAddress(); + return IN6_IS_ADDR_V4MAPPED(&a6.sin6_addr); +} + +/** + * Convert "::ffff:127.0.0.1" to "127.0.0.1". + */ +static SocketAddress +UnmapV4(SocketAddress src, struct sockaddr_in &buffer) +{ + assert(IsV4Mapped(src)); + + const auto &src6 = *(const struct sockaddr_in6 *)src.GetAddress(); + memset(&buffer, 0, sizeof(buffer)); + buffer.sin_family = AF_INET; + memcpy(&buffer.sin_addr, ((const char *)&src6.sin6_addr) + 12, + sizeof(buffer.sin_addr)); + buffer.sin_port = src6.sin6_port; + + return { (const struct sockaddr *)&buffer, sizeof(buffer) }; +} + +#endif + +std::string +ToString(SocketAddress address) +{ +#ifdef HAVE_UN + if (address.GetFamily() == AF_UNIX) + /* return path of UNIX domain sockets */ + return LocalAddressToString(*(const sockaddr_un *)address.GetAddress(), + address.GetSize()); +#endif + +#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) + struct sockaddr_in in_buffer; + if (IsV4Mapped(address)) + address = UnmapV4(address, in_buffer); +#endif + + char host[NI_MAXHOST], serv[NI_MAXSERV]; + int ret = getnameinfo(address.GetAddress(), address.GetSize(), + host, sizeof(host), serv, sizeof(serv), + NI_NUMERICHOST|NI_NUMERICSERV); + if (ret != 0) + return "unknown"; + +#ifdef HAVE_IPV6 + if (strchr(host, ':') != nullptr) { + std::string result("["); + result.append(host); + result.append("]:"); + result.append(serv); + return result; + } +#endif + + std::string result(host); + result.push_back(':'); + result.append(serv); + return result; +} diff --git a/src/net/ToString.hxx b/src/net/ToString.hxx new file mode 100644 index 000000000..4b676008a --- /dev/null +++ b/src/net/ToString.hxx @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NET_TO_STRING_HXX +#define NET_TO_STRING_HXX + +#include "check.h" +#include "Compiler.h" + +#include <string> + +class SocketAddress; + +/** + * Converts the specified socket address into a string in the form + * "IP:PORT". + */ +gcc_pure +std::string +ToString(SocketAddress address); + +#endif diff --git a/src/notify.cxx b/src/notify.cxx index 5b0b4baa0..06a4da611 100644 --- a/src/notify.cxx +++ b/src/notify.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/notify.hxx b/src/notify.hxx index 3e62a0103..b1bc594be 100644 --- a/src/notify.hxx +++ b/src/notify.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ struct notify { Cond cond; bool pending; -#if !defined(WIN32) && !defined(__NetBSD__) && !defined(__BIONIC__) +#ifdef __GLIBC__ constexpr #endif notify():pending(false) {} diff --git a/src/open.h b/src/open.h index b05167188..71629f520 100644 --- a/src/open.h +++ b/src/open.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Domain.cxx b/src/output/Domain.cxx index 878e5f3c5..abfdb6a5e 100644 --- a/src/output/Domain.cxx +++ b/src/output/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Domain.hxx b/src/output/Domain.hxx index e3a20142f..136a2b8a9 100644 --- a/src/output/Domain.hxx +++ b/src/output/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Finish.cxx b/src/output/Finish.cxx index be2ca463e..2dd4acda5 100644 --- a/src/output/Finish.cxx +++ b/src/output/Finish.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Init.cxx b/src/output/Init.cxx index 79ef4f998..ae34bf846 100644 --- a/src/output/Init.cxx +++ b/src/output/Init.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -35,6 +35,7 @@ #include "filter/plugins/ChainFilterPlugin.hxx" #include "config/ConfigError.hxx" #include "config/ConfigGlobal.hxx" +#include "config/Block.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -58,7 +59,7 @@ AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin) filter(nullptr), replay_gain_filter(nullptr), other_replay_gain_filter(nullptr), - command(AO_COMMAND_NONE) + command(Command::NONE) { assert(plugin.finish != nullptr); assert(plugin.open != nullptr); @@ -94,27 +95,27 @@ audio_output_detect(Error &error) * mixer_enabled, if the mixer_type setting is not configured. */ gcc_pure -static enum mixer_type -audio_output_mixer_type(const config_param ¶m) +static MixerType +audio_output_mixer_type(const ConfigBlock &block) { /* read the local "mixer_type" setting */ - const char *p = param.GetBlockValue("mixer_type"); + const char *p = block.GetBlockValue("mixer_type"); if (p != nullptr) return mixer_type_parse(p); /* try the local "mixer_enabled" setting next (deprecated) */ - if (!param.GetBlockValue("mixer_enabled", true)) - return MIXER_TYPE_NONE; + if (!block.GetBlockValue("mixer_enabled", true)) + return MixerType::NONE; /* fall back to the global "mixer_type" setting (also deprecated) */ - return mixer_type_parse(config_get_string(CONF_MIXER_TYPE, + return mixer_type_parse(config_get_string(ConfigOption::MIXER_TYPE, "hardware")); } static Mixer * audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao, - const config_param ¶m, + const ConfigBlock &block, const MixerPlugin *plugin, Filter &filter_chain, MixerListener &listener, @@ -122,22 +123,26 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao, { Mixer *mixer; - switch (audio_output_mixer_type(param)) { - case MIXER_TYPE_NONE: - case MIXER_TYPE_UNKNOWN: + switch (audio_output_mixer_type(block)) { + case MixerType::NONE: + case MixerType::UNKNOWN: return nullptr; - case MIXER_TYPE_HARDWARE: + case MixerType::NULL_: + return mixer_new(event_loop, null_mixer_plugin, ao, listener, + block, error); + + case MixerType::HARDWARE: if (plugin == nullptr) return nullptr; return mixer_new(event_loop, *plugin, ao, listener, - param, error); + block, error); - case MIXER_TYPE_SOFTWARE: + case MixerType::SOFTWARE: mixer = mixer_new(event_loop, software_mixer_plugin, ao, listener, - config_param(), + ConfigBlock(), IgnoreError()); assert(mixer != nullptr); @@ -151,17 +156,17 @@ audio_output_load_mixer(EventLoop &event_loop, AudioOutput &ao, } bool -AudioOutput::Configure(const config_param ¶m, Error &error) +AudioOutput::Configure(const ConfigBlock &block, Error &error) { - if (!param.IsNull()) { - name = param.GetBlockValue(AUDIO_OUTPUT_NAME); + if (!block.IsNull()) { + name = block.GetBlockValue(AUDIO_OUTPUT_NAME); if (name == nullptr) { error.Set(config_domain, "Missing \"name\" configuration"); return false; } - const char *p = param.GetBlockValue(AUDIO_OUTPUT_FORMAT); + const char *p = block.GetBlockValue(AUDIO_OUTPUT_FORMAT); if (p != nullptr) { bool success = audio_format_parse(config_audio_format, @@ -176,9 +181,9 @@ AudioOutput::Configure(const config_param ¶m, Error &error) config_audio_format.Clear(); } - tags = param.GetBlockValue("tags", true); - always_on = param.GetBlockValue("always_on", false); - enabled = param.GetBlockValue("enabled", true); + tags = block.GetBlockValue("tags", true); + always_on = block.GetBlockValue("always_on", false); + enabled = block.GetBlockValue("enabled", true); /* set up the filter chain */ @@ -187,9 +192,9 @@ AudioOutput::Configure(const config_param ¶m, Error &error) /* create the normalization filter (if configured) */ - if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) { + if (config_get_bool(ConfigOption::VOLUME_NORMALIZATION, false)) { Filter *normalize_filter = - filter_new(&normalize_filter_plugin, config_param(), + filter_new(&normalize_filter_plugin, ConfigBlock(), IgnoreError()); assert(normalize_filter != nullptr); @@ -199,7 +204,7 @@ AudioOutput::Configure(const config_param ¶m, Error &error) Error filter_error; filter_chain_parse(*filter, - param.GetBlockValue(AUDIO_FILTERS, ""), + block.GetBlockValue(AUDIO_FILTERS, ""), filter_error); // It's not really fatal - Part of the filter chain has been set up already @@ -217,24 +222,24 @@ AudioOutput::Configure(const config_param ¶m, Error &error) static bool audio_output_setup(EventLoop &event_loop, AudioOutput &ao, MixerListener &mixer_listener, - const config_param ¶m, + const ConfigBlock &block, Error &error) { /* create the replay_gain filter */ const char *replay_gain_handler = - param.GetBlockValue("replay_gain_handler", "software"); + block.GetBlockValue("replay_gain_handler", "software"); if (strcmp(replay_gain_handler, "none") != 0) { ao.replay_gain_filter = filter_new(&replay_gain_filter_plugin, - param, IgnoreError()); + block, IgnoreError()); assert(ao.replay_gain_filter != nullptr); ao.replay_gain_serial = 0; ao.other_replay_gain_filter = filter_new(&replay_gain_filter_plugin, - param, + block, IgnoreError()); assert(ao.other_replay_gain_filter != nullptr); @@ -247,7 +252,7 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao, /* set up the mixer */ Error mixer_error; - ao.mixer = audio_output_load_mixer(event_loop, ao, param, + ao.mixer = audio_output_load_mixer(event_loop, ao, block, ao.plugin.mixer_plugin, *ao.filter, mixer_listener, @@ -275,7 +280,7 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao, /* the "convert" filter must be the last one in the chain */ - ao.convert_filter = filter_new(&convert_filter_plugin, config_param(), + ao.convert_filter = filter_new(&convert_filter_plugin, ConfigBlock(), IgnoreError()); assert(ao.convert_filter != nullptr); @@ -285,17 +290,17 @@ audio_output_setup(EventLoop &event_loop, AudioOutput &ao, } AudioOutput * -audio_output_new(EventLoop &event_loop, const config_param ¶m, +audio_output_new(EventLoop &event_loop, const ConfigBlock &block, MixerListener &mixer_listener, PlayerControl &pc, Error &error) { const AudioOutputPlugin *plugin; - if (!param.IsNull()) { + if (!block.IsNull()) { const char *p; - p = param.GetBlockValue(AUDIO_OUTPUT_TYPE); + p = block.GetBlockValue(AUDIO_OUTPUT_TYPE); if (p == nullptr) { error.Set(config_domain, "Missing \"type\" configuration"); @@ -321,12 +326,12 @@ audio_output_new(EventLoop &event_loop, const config_param ¶m, plugin->name); } - AudioOutput *ao = ao_plugin_init(plugin, param, error); + AudioOutput *ao = ao_plugin_init(plugin, block, error); if (ao == nullptr) return nullptr; if (!audio_output_setup(event_loop, *ao, mixer_listener, - param, error)) { + block, error)) { ao_plugin_finish(ao); return nullptr; } diff --git a/src/output/Internal.hxx b/src/output/Internal.hxx index 6e6ffb442..6b67a8783 100644 --- a/src/output/Internal.hxx +++ b/src/output/Internal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,36 +36,36 @@ class EventLoop; class Mixer; class MixerListener; struct MusicChunk; -struct config_param; +struct ConfigBlock; struct PlayerControl; struct AudioOutputPlugin; -enum audio_output_command { - AO_COMMAND_NONE = 0, - AO_COMMAND_ENABLE, - AO_COMMAND_DISABLE, - AO_COMMAND_OPEN, +struct AudioOutput { + enum class Command { + NONE, + ENABLE, + DISABLE, + OPEN, - /** - * This command is invoked when the input audio format - * changes. - */ - AO_COMMAND_REOPEN, + /** + * This command is invoked when the input audio format + * changes. + */ + REOPEN, - AO_COMMAND_CLOSE, - AO_COMMAND_PAUSE, + CLOSE, + PAUSE, - /** - * Drains the internal (hardware) buffers of the device. This - * operation may take a while to complete. - */ - AO_COMMAND_DRAIN, + /** + * Drains the internal (hardware) buffers of the device. This + * operation may take a while to complete. + */ + DRAIN, - AO_COMMAND_CANCEL, - AO_COMMAND_KILL -}; + CANCEL, + KILL + }; -struct AudioOutput { /** * The device's configured display name. */ @@ -231,7 +231,7 @@ struct AudioOutput { /** * The next command to be performed by the output thread. */ - enum audio_output_command command; + Command command; /** * The music pipe which provides music chunks to be played. @@ -272,7 +272,7 @@ struct AudioOutput { AudioOutput(const AudioOutputPlugin &_plugin); ~AudioOutput(); - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); void StartThread(); void StopThread(); @@ -284,7 +284,7 @@ struct AudioOutput { } bool IsCommandFinished() const { - return command == AO_COMMAND_NONE; + return command == Command::NONE; } /** @@ -299,7 +299,7 @@ struct AudioOutput { * * Caller must lock the mutex. */ - void CommandAsync(audio_output_command cmd); + void CommandAsync(Command cmd); /** * Sends a command to the #AudioOutput object and waits for @@ -307,13 +307,13 @@ struct AudioOutput { * * Caller must lock the mutex. */ - void CommandWait(audio_output_command cmd); + void CommandWait(Command cmd); /** * Lock the #AudioOutput object and execute the command * synchronously. */ - void LockCommandWait(audio_output_command cmd); + void LockCommandWait(Command cmd); /** * Enables the device. @@ -382,6 +382,13 @@ private: void Close(bool drain); void Reopen(); + /** + * Close the output plugin. + * + * Mutex must not be locked. + */ + void CloseOutput(bool drain); + AudioFormat OpenFilter(AudioFormat &format, Error &error_r); /** @@ -430,7 +437,7 @@ private: extern struct notify audio_output_client_notify; AudioOutput * -audio_output_new(EventLoop &event_loop, const config_param ¶m, +audio_output_new(EventLoop &event_loop, const ConfigBlock &block, MixerListener &mixer_listener, PlayerControl &pc, Error &error); diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx index 33ab57894..fc8b3888b 100644 --- a/src/output/MultipleOutputs.cxx +++ b/src/output/MultipleOutputs.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "MultipleOutputs.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "Internal.hxx" #include "Domain.hxx" #include "MusicBuffer.hxx" @@ -27,7 +27,7 @@ #include "MusicChunk.hxx" #include "system/FatalError.hxx" #include "util/Error.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "notify.hxx" @@ -53,16 +53,16 @@ MultipleOutputs::~MultipleOutputs() static AudioOutput * LoadOutput(EventLoop &event_loop, MixerListener &mixer_listener, - PlayerControl &pc, const config_param ¶m) + PlayerControl &pc, const ConfigBlock &block) { Error error; - AudioOutput *output = audio_output_new(event_loop, param, + AudioOutput *output = audio_output_new(event_loop, block, mixer_listener, pc, error); if (output == nullptr) { - if (param.line > 0) + if (block.line > 0) FormatFatalError("line %i: %s", - param.line, + block.line, error.GetMessage()); else FatalError(error); @@ -74,7 +74,7 @@ LoadOutput(EventLoop &event_loop, MixerListener &mixer_listener, void MultipleOutputs::Configure(EventLoop &event_loop, PlayerControl &pc) { - for (const config_param *param = config_get_param(CONF_AUDIO_OUTPUT); + for (const auto *param = config_get_block(ConfigBlockOption::AUDIO_OUTPUT); param != nullptr; param = param->next) { auto output = LoadOutput(event_loop, mixer_listener, pc, *param); @@ -87,7 +87,7 @@ MultipleOutputs::Configure(EventLoop &event_loop, PlayerControl &pc) if (outputs.empty()) { /* auto-detect device */ - const config_param empty; + const ConfigBlock empty; auto output = LoadOutput(event_loop, mixer_listener, pc, empty); outputs.push_back(output); diff --git a/src/output/MultipleOutputs.hxx b/src/output/MultipleOutputs.hxx index 2c6536e2a..8b8360501 100644 --- a/src/output/MultipleOutputs.hxx +++ b/src/output/MultipleOutputs.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -120,7 +120,7 @@ public: * Opens all audio outputs which are not disabled. * * @param audio_format the preferred audio format - * @param buffer the #music_buffer where consumed #MusicChunk objects + * @param _buffer the #music_buffer where consumed #MusicChunk objects * should be returned * @return true on success, false on failure */ diff --git a/src/output/OutputAPI.hxx b/src/output/OutputAPI.hxx index e0fd6eec8..af3f90344 100644 --- a/src/output/OutputAPI.hxx +++ b/src/output/OutputAPI.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #include "Internal.hxx" #include "AudioFormat.hxx" #include "tag/Tag.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" // IWYU pragma: end_exports diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx index e6b8a8e7f..dc7a540a2 100644 --- a/src/output/OutputCommand.cxx +++ b/src/output/OutputCommand.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,7 +28,7 @@ #include "OutputCommand.hxx" #include "MultipleOutputs.hxx" #include "Internal.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "mixer/MixerControl.hxx" #include "mixer/Volume.hxx" #include "Idle.hxx" diff --git a/src/output/OutputCommand.hxx b/src/output/OutputCommand.hxx index 53fc5c95e..5b53cd1c2 100644 --- a/src/output/OutputCommand.hxx +++ b/src/output/OutputCommand.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/OutputControl.cxx b/src/output/OutputControl.cxx index 9eafdb166..d7a114c01 100644 --- a/src/output/OutputControl.cxx +++ b/src/output/OutputControl.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -46,7 +46,7 @@ AudioOutput::WaitForCommand() } void -AudioOutput::CommandAsync(audio_output_command cmd) +AudioOutput::CommandAsync(Command cmd) { assert(IsCommandFinished()); @@ -55,14 +55,14 @@ AudioOutput::CommandAsync(audio_output_command cmd) } void -AudioOutput::CommandWait(audio_output_command cmd) +AudioOutput::CommandWait(Command cmd) { CommandAsync(cmd); WaitForCommand(); } void -AudioOutput::LockCommandWait(audio_output_command cmd) +AudioOutput::LockCommandWait(Command cmd) { const ScopeLock protect(mutex); CommandWait(cmd); @@ -92,7 +92,7 @@ AudioOutput::LockEnableWait() StartThread(); } - LockCommandWait(AO_COMMAND_ENABLE); + LockCommandWait(Command::ENABLE); } void @@ -109,7 +109,7 @@ AudioOutput::LockDisableWait() return; } - LockCommandWait(AO_COMMAND_DISABLE); + LockCommandWait(Command::DISABLE); } inline bool @@ -134,7 +134,7 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp) /* we're not using audio_output_cancel() here, because that function is asynchronous */ - CommandWait(AO_COMMAND_CANCEL); + CommandWait(Command::CANCEL); } return true; @@ -148,7 +148,9 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp) if (!thread.IsDefined()) StartThread(); - CommandWait(open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN); + CommandWait(open + ? Command::REOPEN + : Command::OPEN); const bool open2 = open; if (open2 && mixer != nullptr) { @@ -172,7 +174,7 @@ AudioOutput::CloseWait() assert(!open || !fail_timer.IsDefined()); if (open) - CommandWait(AO_COMMAND_CLOSE); + CommandWait(Command::CLOSE); else fail_timer.Reset(); } @@ -220,7 +222,7 @@ AudioOutput::LockPauseAsync() assert(allow_play); if (IsOpen()) - CommandAsync(AO_COMMAND_PAUSE); + CommandAsync(Command::PAUSE); } void @@ -230,7 +232,7 @@ AudioOutput::LockDrainAsync() assert(allow_play); if (IsOpen()) - CommandAsync(AO_COMMAND_DRAIN); + CommandAsync(Command::DRAIN); } void @@ -240,7 +242,7 @@ AudioOutput::LockCancelAsync() if (IsOpen()) { allow_play = false; - CommandAsync(AO_COMMAND_CANCEL); + CommandAsync(Command::CANCEL); } } @@ -278,7 +280,7 @@ AudioOutput::StopThread() assert(thread.IsDefined()); assert(allow_play); - LockCommandWait(AO_COMMAND_KILL); + LockCommandWait(Command::KILL); thread.Join(); } diff --git a/src/output/OutputControl.hxx b/src/output/OutputControl.hxx index fff3fe406..5c8f49718 100644 --- a/src/output/OutputControl.hxx +++ b/src/output/OutputControl.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/OutputPlugin.cxx b/src/output/OutputPlugin.cxx index 33bb854d4..7d95ef345 100644 --- a/src/output/OutputPlugin.cxx +++ b/src/output/OutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,13 +23,13 @@ AudioOutput * ao_plugin_init(const AudioOutputPlugin *plugin, - const config_param ¶m, + const ConfigBlock &block, Error &error) { assert(plugin != nullptr); assert(plugin->init != nullptr); - return plugin->init(param, error); + return plugin->init(block, error); } void @@ -75,7 +75,7 @@ ao_plugin_delay(AudioOutput *ao) } void -ao_plugin_send_tag(AudioOutput *ao, const Tag *tag) +ao_plugin_send_tag(AudioOutput *ao, const Tag &tag) { if (ao->plugin.send_tag != nullptr) ao->plugin.send_tag(ao, tag); diff --git a/src/output/OutputPlugin.hxx b/src/output/OutputPlugin.hxx index 00fa36bc0..2f6869368 100644 --- a/src/output/OutputPlugin.hxx +++ b/src/output/OutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,7 +24,7 @@ #include <stddef.h> -struct config_param; +struct ConfigBlock; struct AudioFormat; struct Tag; struct AudioOutput; @@ -44,7 +44,7 @@ struct AudioOutputPlugin { * Test if this plugin can provide a default output, in case * none has been configured. This method is optional. */ - bool (*test_default_device)(void); + bool (*test_default_device)(); /** * Configure and initialize the device, but do not open it @@ -55,8 +55,7 @@ struct AudioOutputPlugin { * @return nullptr on error, or an opaque pointer to the plugin's * data */ - AudioOutput *(*init)(const config_param ¶m, - Error &error); + AudioOutput *(*init)(const ConfigBlock &block, Error &error); /** * Free resources allocated by this device. @@ -107,7 +106,7 @@ struct AudioOutputPlugin { * Display metadata for the next chunk. Optional method, * because not all devices can display metadata. */ - void (*send_tag)(AudioOutput *data, const Tag *tag); + void (*send_tag)(AudioOutput *data, const Tag &tag); /** * Play a chunk of audio data. @@ -162,7 +161,7 @@ ao_plugin_test_default_device(const AudioOutputPlugin *plugin) gcc_malloc AudioOutput * ao_plugin_init(const AudioOutputPlugin *plugin, - const config_param ¶m, + const ConfigBlock &block, Error &error); void @@ -186,7 +185,7 @@ unsigned ao_plugin_delay(AudioOutput *ao); void -ao_plugin_send_tag(AudioOutput *ao, const Tag *tag); +ao_plugin_send_tag(AudioOutput *ao, const Tag &tag); size_t ao_plugin_play(AudioOutput *ao, const void *chunk, size_t size, diff --git a/src/output/OutputPrint.cxx b/src/output/OutputPrint.cxx index 414a86e32..d2ddbbf8b 100644 --- a/src/output/OutputPrint.cxx +++ b/src/output/OutputPrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,18 +26,17 @@ #include "OutputPrint.hxx" #include "MultipleOutputs.hxx" #include "Internal.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" void -printAudioDevices(Client &client, const MultipleOutputs &outputs) +printAudioDevices(Response &r, const MultipleOutputs &outputs) { for (unsigned i = 0, n = outputs.Size(); i != n; ++i) { const AudioOutput &ao = outputs.Get(i); - client_printf(client, - "outputid: %i\n" - "outputname: %s\n" - "outputenabled: %i\n", - i, ao.name, ao.enabled); + r.Format("outputid: %i\n" + "outputname: %s\n" + "outputenabled: %i\n", + i, ao.name, ao.enabled); } } diff --git a/src/output/OutputPrint.hxx b/src/output/OutputPrint.hxx index 29aa2b11c..e05c8efd5 100644 --- a/src/output/OutputPrint.hxx +++ b/src/output/OutputPrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,10 +25,10 @@ #ifndef MPD_OUTPUT_PRINT_HXX #define MPD_OUTPUT_PRINT_HXX -class Client; +class Response; class MultipleOutputs; void -printAudioDevices(Client &client, const MultipleOutputs &outputs); +printAudioDevices(Response &r, const MultipleOutputs &outputs); #endif diff --git a/src/output/OutputState.cxx b/src/output/OutputState.cxx index fb01b1c65..a1ca11b49 100644 --- a/src/output/OutputState.cxx +++ b/src/output/OutputState.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/OutputState.hxx b/src/output/OutputState.hxx index 47f8429d5..45076d59f 100644 --- a/src/output/OutputState.hxx +++ b/src/output/OutputState.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -41,6 +41,6 @@ audio_output_state_save(BufferedOutputStream &os, * whether the state has changed and the state file should be saved. */ unsigned -audio_output_state_get_version(void); +audio_output_state_get_version(); #endif diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx index 2ec0670c1..67205aa4c 100644 --- a/src/output/OutputThread.cxx +++ b/src/output/OutputThread.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,7 +27,7 @@ #include "filter/FilterInternal.hxx" #include "filter/plugins/ConvertFilterPlugin.hxx" #include "filter/plugins/ReplayGainFilterPlugin.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "MusicPipe.hxx" #include "MusicChunk.hxx" #include "thread/Util.hxx" @@ -45,8 +45,8 @@ void AudioOutput::CommandFinished() { - assert(command != AO_COMMAND_NONE); - command = AO_COMMAND_NONE; + assert(command != Command::NONE); + command = Command::NONE; mutex.unlock(); audio_output_client_notify.Signal(); @@ -251,12 +251,7 @@ AudioOutput::Close(bool drain) mutex.unlock(); - if (drain) - ao_plugin_drain(this); - else - ao_plugin_cancel(this); - - ao_plugin_close(this); + CloseOutput(drain); CloseFilter(); mutex.lock(); @@ -265,6 +260,17 @@ AudioOutput::Close(bool drain) plugin.name, name); } +inline void +AudioOutput::CloseOutput(bool drain) +{ + if (drain) + ao_plugin_drain(this); + else + ao_plugin_cancel(this); + + ao_plugin_close(this); +} + void AudioOutput::ReopenFilter() { @@ -342,7 +348,7 @@ AudioOutput::WaitForDelay() (void)cond.timed_wait(mutex, delay); - if (command != AO_COMMAND_NONE) + if (command != Command::NONE) return false; } } @@ -455,7 +461,7 @@ AudioOutput::PlayChunk(const MusicChunk *chunk) if (tags && gcc_unlikely(chunk->tag != nullptr)) { mutex.unlock(); - ao_plugin_send_tag(this, chunk->tag); + ao_plugin_send_tag(this, *chunk->tag); mutex.lock(); } @@ -471,7 +477,7 @@ AudioOutput::PlayChunk(const MusicChunk *chunk) Error error; - while (!data.IsEmpty() && command == AO_COMMAND_NONE) { + while (!data.IsEmpty() && command == Command::NONE) { if (!WaitForDelay()) break; @@ -529,7 +535,7 @@ AudioOutput::Play() assert(!in_playback_loop); in_playback_loop = true; - while (chunk != nullptr && command == AO_COMMAND_NONE) { + while (chunk != nullptr && command == Command::NONE) { assert(!current_chunk_finished); current_chunk = chunk; @@ -577,7 +583,7 @@ AudioOutput::Pause() Close(false); break; } - } while (command == AO_COMMAND_NONE); + } while (command == Command::NONE); pause = false; } @@ -594,30 +600,30 @@ AudioOutput::Task() while (1) { switch (command) { - case AO_COMMAND_NONE: + case Command::NONE: break; - case AO_COMMAND_ENABLE: + case Command::ENABLE: Enable(); CommandFinished(); break; - case AO_COMMAND_DISABLE: + case Command::DISABLE: Disable(); CommandFinished(); break; - case AO_COMMAND_OPEN: + case Command::OPEN: Open(); CommandFinished(); break; - case AO_COMMAND_REOPEN: + case Command::REOPEN: Reopen(); CommandFinished(); break; - case AO_COMMAND_CLOSE: + case Command::CLOSE: assert(open); assert(pipe != nullptr); @@ -625,7 +631,7 @@ AudioOutput::Task() CommandFinished(); break; - case AO_COMMAND_PAUSE: + case Command::PAUSE: if (!open) { /* the output has failed after audio_output_all_pause() has @@ -642,7 +648,7 @@ AudioOutput::Task() the new command first */ continue; - case AO_COMMAND_DRAIN: + case Command::DRAIN: if (open) { assert(current_chunk == nullptr); assert(pipe->Peek() == nullptr); @@ -655,7 +661,7 @@ AudioOutput::Task() CommandFinished(); continue; - case AO_COMMAND_CANCEL: + case Command::CANCEL: current_chunk = nullptr; if (open) { @@ -667,7 +673,7 @@ AudioOutput::Task() CommandFinished(); continue; - case AO_COMMAND_KILL: + case Command::KILL: current_chunk = nullptr; CommandFinished(); mutex.unlock(); @@ -679,7 +685,7 @@ AudioOutput::Task() chunks in the pipe */ continue; - if (command == AO_COMMAND_NONE) { + if (command == Command::NONE) { woken_for_play = false; cond.wait(mutex); } @@ -696,7 +702,7 @@ AudioOutput::Task(void *arg) void AudioOutput::StartThread() { - assert(command == AO_COMMAND_NONE); + assert(command == Command::NONE); Error error; if (!thread.Start(Task, this, error)) diff --git a/src/output/Registry.cxx b/src/output/Registry.cxx index 566f6b6a8..b1734983e 100644 --- a/src/output/Registry.cxx +++ b/src/output/Registry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "plugins/AoOutputPlugin.hxx" #include "plugins/FifoOutputPlugin.hxx" #include "plugins/httpd/HttpdOutputPlugin.hxx" +#include "plugins/HaikuOutputPlugin.hxx" #include "plugins/JackOutputPlugin.hxx" #include "plugins/NullOutputPlugin.hxx" #include "plugins/OpenALOutputPlugin.hxx" @@ -51,16 +52,19 @@ const AudioOutputPlugin *const audio_output_plugins[] = { #ifdef HAVE_FIFO &fifo_output_plugin, #endif +#ifdef HAVE_HAIKU + &haiku_output_plugin, +#endif #ifdef ENABLE_PIPE_OUTPUT &pipe_output_plugin, #endif -#ifdef HAVE_ALSA +#ifdef ENABLE_ALSA &alsa_output_plugin, #endif -#ifdef HAVE_ROAR +#ifdef ENABLE_ROAR &roar_output_plugin, #endif -#ifdef HAVE_AO +#ifdef ENABLE_AO &ao_output_plugin, #endif #ifdef HAVE_OSS @@ -75,10 +79,10 @@ const AudioOutputPlugin *const audio_output_plugins[] = { #ifdef ENABLE_SOLARIS_OUTPUT &solaris_output_plugin, #endif -#ifdef HAVE_PULSE +#ifdef ENABLE_PULSE &pulse_output_plugin, #endif -#ifdef HAVE_JACK +#ifdef ENABLE_JACK &jack_output_plugin, #endif #ifdef ENABLE_HTTPD_OUTPUT diff --git a/src/output/Registry.hxx b/src/output/Registry.hxx index bc9c1ae2b..2c7202a75 100644 --- a/src/output/Registry.hxx +++ b/src/output/Registry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Timer.cxx b/src/output/Timer.cxx index d3dcc714d..a75744744 100644 --- a/src/output/Timer.cxx +++ b/src/output/Timer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Timer.hxx b/src/output/Timer.hxx index 3c935cfac..057090c1e 100644 --- a/src/output/Timer.hxx +++ b/src/output/Timer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/Wrapper.hxx b/src/output/Wrapper.hxx new file mode 100644 index 000000000..c043849bb --- /dev/null +++ b/src/output/Wrapper.hxx @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2003-2015 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_OUTPUT_WRAPPER_HXX +#define MPD_OUTPUT_WRAPPER_HXX + +#include "util/Cast.hxx" + +struct ConfigBlock; + +template<class T> +struct AudioOutputWrapper { + static T &Cast(AudioOutput &ao) { + return ContainerCast(ao, &T::base); + } + + static AudioOutput *Init(const ConfigBlock &block, Error &error) { + T *t = T::Create(block, error); + return t != nullptr + ? &t->base + : nullptr; + } + + static void Finish(AudioOutput *ao) { + T *t = &Cast(*ao); + delete t; + } + + static bool Enable(AudioOutput *ao, Error &error) { + T &t = Cast(*ao); + return t.Enable(error); + } + + static void Disable(AudioOutput *ao) { + T &t = Cast(*ao); + t.Disable(); + } + + static bool Open(AudioOutput *ao, AudioFormat &audio_format, + Error &error) { + T &t = Cast(*ao); + return t.Open(audio_format, error); + } + + static void Close(AudioOutput *ao) { + T &t = Cast(*ao); + t.Close(); + } + + gcc_pure + static unsigned Delay(AudioOutput *ao) { + T &t = Cast(*ao); + return t.Delay(); + } + + gcc_pure + static void SendTag(AudioOutput *ao, const Tag &tag) { + T &t = Cast(*ao); + t.SendTag(tag); + } + + static size_t Play(AudioOutput *ao, const void *chunk, size_t size, + Error &error) { + T &t = Cast(*ao); + return t.Play(chunk, size, error); + } + + static void Drain(AudioOutput *ao) { + T &t = Cast(*ao); + t.Drain(); + } + + static void Cancel(AudioOutput *ao) { + T &t = Cast(*ao); + t.Cancel(); + } + + gcc_pure + static bool Pause(AudioOutput *ao) { + T &t = Cast(*ao); + return t.Pause(); + } +}; + +#endif diff --git a/src/output/plugins/AlsaOutputPlugin.cxx b/src/output/plugins/AlsaOutputPlugin.cxx index 28c374a00..8a7bb9643 100644 --- a/src/output/plugins/AlsaOutputPlugin.cxx +++ b/src/output/plugins/AlsaOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "AlsaOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "pcm/PcmExport.hxx" #include "config/ConfigError.hxx" @@ -131,92 +132,108 @@ struct AlsaOutput { mode(0), writei(snd_pcm_writei) { } - bool Configure(const config_param ¶m, Error &error); + ~AlsaOutput() { + /* free libasound's config cache */ + snd_config_update_free_global(); + } + + gcc_pure + const char *GetDevice() { + return device.empty() ? default_device : device.c_str(); + } + + bool Configure(const ConfigBlock &block, Error &error); + static AlsaOutput *Create(const ConfigBlock &block, Error &error); + + bool Enable(Error &error); + void Disable(); + + bool Open(AudioFormat &audio_format, Error &error); + void Close(); + + size_t Play(const void *chunk, size_t size, Error &error); + void Drain(); + void Cancel(); + +private: + bool SetupDop(AudioFormat audio_format, + bool *shift8_r, bool *packed_r, bool *reverse_endian_r, + Error &error); + bool SetupOrDop(AudioFormat &audio_format, Error &error); + + int Recover(int err); + + /** + * Write silence to the ALSA device. + */ + void WriteSilence(snd_pcm_uframes_t nframes) { + writei(pcm, silence, nframes); + } + }; static constexpr Domain alsa_output_domain("alsa_output"); -static const char * -alsa_device(const AlsaOutput *ad) -{ - return ad->device.empty() ? default_device : ad->device.c_str(); -} - inline bool -AlsaOutput::Configure(const config_param ¶m, Error &error) +AlsaOutput::Configure(const ConfigBlock &block, Error &error) { - if (!base.Configure(param, error)) + if (!base.Configure(block, error)) return false; - device = param.GetBlockValue("device", ""); + device = block.GetBlockValue("device", ""); - use_mmap = param.GetBlockValue("use_mmap", false); + use_mmap = block.GetBlockValue("use_mmap", false); - dop = param.GetBlockValue("dop", false) || + dop = block.GetBlockValue("dop", false) || /* legacy name from MPD 0.18 and older: */ - param.GetBlockValue("dsd_usb", false); + block.GetBlockValue("dsd_usb", false); - buffer_time = param.GetBlockValue("buffer_time", + buffer_time = block.GetBlockValue("buffer_time", MPD_ALSA_BUFFER_TIME_US); - period_time = param.GetBlockValue("period_time", 0u); + period_time = block.GetBlockValue("period_time", 0u); #ifdef SND_PCM_NO_AUTO_RESAMPLE - if (!param.GetBlockValue("auto_resample", true)) + if (!block.GetBlockValue("auto_resample", true)) mode |= SND_PCM_NO_AUTO_RESAMPLE; #endif #ifdef SND_PCM_NO_AUTO_CHANNELS - if (!param.GetBlockValue("auto_channels", true)) + if (!block.GetBlockValue("auto_channels", true)) mode |= SND_PCM_NO_AUTO_CHANNELS; #endif #ifdef SND_PCM_NO_AUTO_FORMAT - if (!param.GetBlockValue("auto_format", true)) + if (!block.GetBlockValue("auto_format", true)) mode |= SND_PCM_NO_AUTO_FORMAT; #endif return true; } -static AudioOutput * -alsa_init(const config_param ¶m, Error &error) +inline AlsaOutput * +AlsaOutput::Create(const ConfigBlock &block, Error &error) { AlsaOutput *ad = new AlsaOutput(); - if (!ad->Configure(param, error)) { + if (!ad->Configure(block, error)) { delete ad; return nullptr; } - return &ad->base; + return ad; } -static void -alsa_finish(AudioOutput *ao) -{ - AlsaOutput *ad = (AlsaOutput *)ao; - - delete ad; - - /* free libasound's config cache */ - snd_config_update_free_global(); -} - -static bool -alsa_output_enable(AudioOutput *ao, gcc_unused Error &error) +inline bool +AlsaOutput::Enable(gcc_unused Error &error) { - AlsaOutput *ad = (AlsaOutput *)ao; - - ad->pcm_export.Construct(); + pcm_export.Construct(); return true; } -static void -alsa_output_disable(AudioOutput *ao) +inline void +AlsaOutput::Disable() { - AlsaOutput *ad = (AlsaOutput *)ao; - - ad->pcm_export.Destruct(); + pcm_export.Destruct(); } static bool @@ -450,7 +467,7 @@ configure_hw: if (err < 0) { FormatWarning(alsa_output_domain, "Cannot set mmap'ed mode on ALSA device \"%s\": %s", - alsa_device(ad), snd_strerror(-err)); + ad->GetDevice(), snd_strerror(-err)); LogWarning(alsa_output_domain, "Falling back to direct write mode"); ad->use_mmap = false; @@ -472,7 +489,7 @@ configure_hw: if (err < 0) { error.Format(alsa_output_domain, err, "ALSA device \"%s\" does not support format %s: %s", - alsa_device(ad), + ad->GetDevice(), sample_format_to_string(audio_format.format), snd_strerror(-err)); return false; @@ -489,7 +506,7 @@ configure_hw: if (err < 0) { error.Format(alsa_output_domain, err, "ALSA device \"%s\" does not support %i channels: %s", - alsa_device(ad), (int)audio_format.channels, + ad->GetDevice(), (int)audio_format.channels, snd_strerror(-err)); return false; } @@ -500,7 +517,7 @@ configure_hw: if (err < 0 || sample_rate == 0) { error.Format(alsa_output_domain, err, "ALSA device \"%s\" does not support %u Hz audio", - alsa_device(ad), audio_format.sample_rate); + ad->GetDevice(), audio_format.sample_rate); return false; } audio_format.sample_rate = sample_rate; @@ -631,16 +648,16 @@ configure_hw: error: error.Format(alsa_output_domain, err, "Error opening ALSA device \"%s\" (%s): %s", - alsa_device(ad), cmd, snd_strerror(-err)); + ad->GetDevice(), cmd, snd_strerror(-err)); return false; } -static bool -alsa_setup_dop(AlsaOutput *ad, const AudioFormat audio_format, - bool *shift8_r, bool *packed_r, bool *reverse_endian_r, - Error &error) +inline bool +AlsaOutput::SetupDop(const AudioFormat audio_format, + bool *shift8_r, bool *packed_r, bool *reverse_endian_r, + Error &error) { - assert(ad->dop); + assert(dop); assert(audio_format.format == SampleFormat::DSD); /* pass 24 bit to alsa_setup() */ @@ -651,7 +668,7 @@ alsa_setup_dop(AlsaOutput *ad, const AudioFormat audio_format, const AudioFormat check = dop_format; - if (!alsa_setup(ad, dop_format, packed_r, reverse_endian_r, error)) + if (!alsa_setup(this, dop_format, packed_r, reverse_endian_r, error)) return false; /* if the device allows only 32 bit, shift all DoP @@ -668,102 +685,91 @@ alsa_setup_dop(AlsaOutput *ad, const AudioFormat audio_format, for DSD over USB */ error.Format(alsa_output_domain, "Failed to configure DSD-over-PCM on ALSA device \"%s\"", - alsa_device(ad)); - delete[] ad->silence; + GetDevice()); + delete[] silence; return false; } return true; } -static bool -alsa_setup_or_dop(AlsaOutput *ad, AudioFormat &audio_format, - Error &error) +inline bool +AlsaOutput::SetupOrDop(AudioFormat &audio_format, Error &error) { bool shift8 = false, packed, reverse_endian; - const bool dop = ad->dop && + const bool dop2 = dop && audio_format.format == SampleFormat::DSD; - const bool success = dop - ? alsa_setup_dop(ad, audio_format, - &shift8, &packed, &reverse_endian, - error) - : alsa_setup(ad, audio_format, &packed, &reverse_endian, + const bool success = dop2 + ? SetupDop(audio_format, + &shift8, &packed, &reverse_endian, + error) + : alsa_setup(this, audio_format, &packed, &reverse_endian, error); if (!success) return false; - ad->pcm_export->Open(audio_format.format, - audio_format.channels, - dop, shift8, packed, reverse_endian); + pcm_export->Open(audio_format.format, + audio_format.channels, + dop2, shift8, packed, reverse_endian); return true; } -static bool -alsa_open(AudioOutput *ao, AudioFormat &audio_format, Error &error) +inline bool +AlsaOutput::Open(AudioFormat &audio_format, Error &error) { - AlsaOutput *ad = (AlsaOutput *)ao; - - int err = snd_pcm_open(&ad->pcm, alsa_device(ad), - SND_PCM_STREAM_PLAYBACK, ad->mode); + int err = snd_pcm_open(&pcm, GetDevice(), + SND_PCM_STREAM_PLAYBACK, mode); if (err < 0) { error.Format(alsa_output_domain, err, "Failed to open ALSA device \"%s\": %s", - alsa_device(ad), snd_strerror(err)); + GetDevice(), snd_strerror(err)); return false; } FormatDebug(alsa_output_domain, "opened %s type=%s", - snd_pcm_name(ad->pcm), - snd_pcm_type_name(snd_pcm_type(ad->pcm))); + snd_pcm_name(pcm), + snd_pcm_type_name(snd_pcm_type(pcm))); - if (!alsa_setup_or_dop(ad, audio_format, error)) { - snd_pcm_close(ad->pcm); + if (!SetupOrDop(audio_format, error)) { + snd_pcm_close(pcm); return false; } - ad->in_frame_size = audio_format.GetFrameSize(); - ad->out_frame_size = ad->pcm_export->GetFrameSize(audio_format); + in_frame_size = audio_format.GetFrameSize(); + out_frame_size = pcm_export->GetFrameSize(audio_format); - ad->must_prepare = false; + must_prepare = false; return true; } -/** - * Write silence to the ALSA device. - */ -static void -alsa_write_silence(AlsaOutput *ad, snd_pcm_uframes_t nframes) -{ - ad->writei(ad->pcm, ad->silence, nframes); -} - -static int -alsa_recover(AlsaOutput *ad, int err) +inline int +AlsaOutput::Recover(int err) { if (err == -EPIPE) { FormatDebug(alsa_output_domain, - "Underrun on ALSA device \"%s\"", alsa_device(ad)); + "Underrun on ALSA device \"%s\"", + GetDevice()); } else if (err == -ESTRPIPE) { FormatDebug(alsa_output_domain, "ALSA device \"%s\" was suspended", - alsa_device(ad)); + GetDevice()); } - switch (snd_pcm_state(ad->pcm)) { + switch (snd_pcm_state(pcm)) { case SND_PCM_STATE_PAUSED: - err = snd_pcm_pause(ad->pcm, /* disable */ 0); + err = snd_pcm_pause(pcm, /* disable */ 0); break; case SND_PCM_STATE_SUSPENDED: - err = snd_pcm_resume(ad->pcm); + err = snd_pcm_resume(pcm); if (err == -EAGAIN) return 0; /* fall-through to snd_pcm_prepare: */ case SND_PCM_STATE_SETUP: case SND_PCM_STATE_XRUN: - ad->period_position = 0; - err = snd_pcm_prepare(ad->pcm); + period_position = 0; + err = snd_pcm_prepare(pcm); break; case SND_PCM_STATE_DISCONNECTED: break; @@ -779,67 +785,58 @@ alsa_recover(AlsaOutput *ad, int err) return err; } -static void -alsa_drain(AudioOutput *ao) +inline void +AlsaOutput::Drain() { - AlsaOutput *ad = (AlsaOutput *)ao; - - if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING) + if (snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) return; - if (ad->period_position > 0) { + if (period_position > 0) { /* generate some silence to finish the partial period */ snd_pcm_uframes_t nframes = - ad->period_frames - ad->period_position; - alsa_write_silence(ad, nframes); + period_frames - period_position; + WriteSilence(nframes); } - snd_pcm_drain(ad->pcm); + snd_pcm_drain(pcm); - ad->period_position = 0; + period_position = 0; } -static void -alsa_cancel(AudioOutput *ao) +inline void +AlsaOutput::Cancel() { - AlsaOutput *ad = (AlsaOutput *)ao; + period_position = 0; + must_prepare = true; - ad->period_position = 0; - ad->must_prepare = true; - - snd_pcm_drop(ad->pcm); + snd_pcm_drop(pcm); } -static void -alsa_close(AudioOutput *ao) +inline void +AlsaOutput::Close() { - AlsaOutput *ad = (AlsaOutput *)ao; - - snd_pcm_close(ad->pcm); - delete[] ad->silence; + snd_pcm_close(pcm); + delete[] silence; } -static size_t -alsa_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +AlsaOutput::Play(const void *chunk, size_t size, Error &error) { - AlsaOutput *ad = (AlsaOutput *)ao; - assert(size > 0); - assert(size % ad->in_frame_size == 0); + assert(size % in_frame_size == 0); - if (ad->must_prepare) { - ad->must_prepare = false; + if (must_prepare) { + must_prepare = false; - int err = snd_pcm_prepare(ad->pcm); + int err = snd_pcm_prepare(pcm); if (err < 0) { error.Set(alsa_output_domain, err, snd_strerror(-err)); return 0; } } - const auto e = ad->pcm_export->Export({chunk, size}); + const auto e = pcm_export->Export({chunk, size}); if (e.size == 0) /* the DoP (DSD over PCM) filter converts two frames at a time and ignores the last odd frame; if there @@ -852,43 +849,45 @@ alsa_play(AudioOutput *ao, const void *chunk, size_t size, chunk = e.data; size = e.size; - assert(size % ad->out_frame_size == 0); + assert(size % out_frame_size == 0); - size /= ad->out_frame_size; + size /= out_frame_size; assert(size > 0); while (true) { - snd_pcm_sframes_t ret = ad->writei(ad->pcm, chunk, size); + snd_pcm_sframes_t ret = writei(pcm, chunk, size); if (ret > 0) { - ad->period_position = (ad->period_position + ret) - % ad->period_frames; + period_position = (period_position + ret) + % period_frames; - size_t bytes_written = ret * ad->out_frame_size; - return ad->pcm_export->CalcSourceSize(bytes_written); + size_t bytes_written = ret * out_frame_size; + return pcm_export->CalcSourceSize(bytes_written); } if (ret < 0 && ret != -EAGAIN && ret != -EINTR && - alsa_recover(ad, ret) < 0) { + Recover(ret) < 0) { error.Set(alsa_output_domain, ret, snd_strerror(-ret)); return 0; } } } +typedef AudioOutputWrapper<AlsaOutput> Wrapper; + const struct AudioOutputPlugin alsa_output_plugin = { "alsa", alsa_test_default_device, - alsa_init, - alsa_finish, - alsa_output_enable, - alsa_output_disable, - alsa_open, - alsa_close, + &Wrapper::Init, + &Wrapper::Finish, + &Wrapper::Enable, + &Wrapper::Disable, + &Wrapper::Open, + &Wrapper::Close, nullptr, nullptr, - alsa_play, - alsa_drain, - alsa_cancel, + &Wrapper::Play, + &Wrapper::Drain, + &Wrapper::Cancel, nullptr, &alsa_mixer_plugin, diff --git a/src/output/plugins/AlsaOutputPlugin.hxx b/src/output/plugins/AlsaOutputPlugin.hxx index f72116f91..ff7d439a9 100644 --- a/src/output/plugins/AlsaOutputPlugin.hxx +++ b/src/output/plugins/AlsaOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/AoOutputPlugin.cxx b/src/output/plugins/AoOutputPlugin.cxx index af8c88fa1..3c0cf74a4 100644 --- a/src/output/plugins/AoOutputPlugin.cxx +++ b/src/output/plugins/AoOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,12 +20,13 @@ #include "config.h" #include "AoOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "util/DivideString.hxx" +#include "util/SplitString.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" #include <ao/ao.h> -#include <glib.h> #include <string.h> @@ -45,11 +46,11 @@ struct AoOutput { AoOutput() :base(ao_output_plugin) {} - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); } - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); }; static constexpr Domain ao_output_domain("ao_output"); @@ -89,20 +90,20 @@ ao_output_error(Error &error_r) } inline bool -AoOutput::Configure(const config_param ¶m, Error &error) +AoOutput::Configure(const ConfigBlock &block, Error &error) { const char *value; options = nullptr; - write_size = param.GetBlockValue("write_size", 1024u); + write_size = block.GetBlockValue("write_size", 1024u); if (ao_output_ref == 0) { ao_initialize(); } ao_output_ref++; - value = param.GetBlockValue("driver", "default"); + value = block.GetBlockValue("driver", "default"); if (0 == strcmp(value, "default")) driver = ao_default_driver_id(); else @@ -122,45 +123,38 @@ AoOutput::Configure(const config_param ¶m, Error &error) } FormatDebug(ao_output_domain, "using ao driver \"%s\" for \"%s\"\n", - ai->short_name, param.GetBlockValue("name", nullptr)); + ai->short_name, block.GetBlockValue("name", nullptr)); - value = param.GetBlockValue("options", nullptr); + value = block.GetBlockValue("options", nullptr); if (value != nullptr) { - gchar **_options = g_strsplit(value, ";", 0); + for (const auto &i : SplitString(value, ';')) { + const DivideString ss(i.c_str(), '=', true); - for (unsigned i = 0; _options[i] != nullptr; ++i) { - gchar **key_value = g_strsplit(_options[i], "=", 2); - - if (key_value[0] == nullptr || key_value[1] == nullptr) { + if (!ss.IsDefined()) { error.Format(ao_output_domain, "problems parsing options \"%s\"", - _options[i]); + i.c_str()); return false; } - ao_append_option(&options, key_value[0], - key_value[1]); - - g_strfreev(key_value); + ao_append_option(&options, ss.GetFirst(), ss.GetSecond()); } - - g_strfreev(_options); } return true; } static AudioOutput * -ao_output_init(const config_param ¶m, Error &error) +ao_output_init(const ConfigBlock &block, Error &error) { AoOutput *ad = new AoOutput(); - if (!ad->Initialize(param, error)) { + if (!ad->Initialize(block, error)) { delete ad; return nullptr; } - if (!ad->Configure(param, error)) { + if (!ad->Configure(block, error)) { delete ad; return nullptr; } diff --git a/src/output/plugins/AoOutputPlugin.hxx b/src/output/plugins/AoOutputPlugin.hxx index 07c2ba16b..582070c47 100644 --- a/src/output/plugins/AoOutputPlugin.hxx +++ b/src/output/plugins/AoOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/FifoOutputPlugin.cxx b/src/output/plugins/FifoOutputPlugin.cxx index 9df5a74dd..d4019df53 100644 --- a/src/output/plugins/FifoOutputPlugin.cxx +++ b/src/output/plugins/FifoOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,9 +21,11 @@ #include "FifoOutputPlugin.hxx" #include "config/ConfigError.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "../Timer.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" @@ -33,9 +35,9 @@ #include <errno.h> #include <unistd.h> -#define FIFO_BUFFER_SIZE 65536 /* pipe capacity on Linux >= 2.6.11 */ +class FifoOutput { + friend struct AudioOutputWrapper<FifoOutput>; -struct FifoOutput { AudioOutput base; AllocatedPath path; @@ -46,21 +48,35 @@ struct FifoOutput { bool created; Timer *timer; +public: FifoOutput() :base(fifo_output_plugin), path(AllocatedPath::Null()), input(-1), output(-1), created(false) {} - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); + ~FifoOutput() { + CloseFifo(); } + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); + } + + static FifoOutput *Create(const ConfigBlock &block, Error &error); + bool Create(Error &error); bool Check(Error &error); void Delete(); - bool Open(Error &error); + bool OpenFifo(Error &error); + void CloseFifo(); + + bool Open(AudioFormat &audio_format, Error &error); void Close(); + + unsigned Delay() const; + size_t Play(const void *chunk, size_t size, Error &error); + void Cancel(); }; static constexpr Domain fifo_output_domain("fifo_output"); @@ -82,7 +98,7 @@ FifoOutput::Delete() } void -FifoOutput::Close() +FifoOutput::CloseFifo() { if (input >= 0) { close(input); @@ -94,8 +110,8 @@ FifoOutput::Close() output = -1; } - struct stat st; - if (created && StatFile(path, st)) + FileInfo fi; + if (created && GetFileInfo(path, fi)) Delete(); } @@ -138,7 +154,7 @@ FifoOutput::Check(Error &error) } inline bool -FifoOutput::Open(Error &error) +FifoOutput::OpenFifo(Error &error) { if (!Check(error)) return false; @@ -147,7 +163,7 @@ FifoOutput::Open(Error &error) if (input < 0) { error.FormatErrno("Could not open FIFO \"%s\" for reading", path_utf8.c_str()); - Close(); + CloseFifo(); return false; } @@ -155,25 +171,19 @@ FifoOutput::Open(Error &error) if (output < 0) { error.FormatErrno("Could not open FIFO \"%s\" for writing", path_utf8.c_str()); - Close(); + CloseFifo(); return false; } return true; } -static bool -fifo_open(FifoOutput *fd, Error &error) -{ - return fd->Open(error); -} - -static AudioOutput * -fifo_output_init(const config_param ¶m, Error &error) +inline FifoOutput * +FifoOutput::Create(const ConfigBlock &block, Error &error) { FifoOutput *fd = new FifoOutput(); - fd->path = param.GetBlockPath("path", error); + fd->path = block.GetBlockPath("path", error); if (fd->path.IsNull()) { delete fd; @@ -185,89 +195,67 @@ fifo_output_init(const config_param ¶m, Error &error) fd->path_utf8 = fd->path.ToUTF8(); - if (!fd->Initialize(param, error)) { + if (!fd->Initialize(block, error)) { delete fd; return nullptr; } - if (!fifo_open(fd, error)) { + if (!fd->OpenFifo(error)) { delete fd; return nullptr; } - return &fd->base; -} - -static void -fifo_output_finish(AudioOutput *ao) -{ - FifoOutput *fd = (FifoOutput *)ao; - - fd->Close(); - delete fd; + return fd; } -static bool -fifo_output_open(AudioOutput *ao, AudioFormat &audio_format, - gcc_unused Error &error) +bool +FifoOutput::Open(AudioFormat &audio_format, gcc_unused Error &error) { - FifoOutput *fd = (FifoOutput *)ao; - - fd->timer = new Timer(audio_format); - + timer = new Timer(audio_format); return true; } -static void -fifo_output_close(AudioOutput *ao) +void +FifoOutput::Close() { - FifoOutput *fd = (FifoOutput *)ao; - - delete fd->timer; + delete timer; } -static void -fifo_output_cancel(AudioOutput *ao) +inline void +FifoOutput::Cancel() { - FifoOutput *fd = (FifoOutput *)ao; - char buf[FIFO_BUFFER_SIZE]; - int bytes = 1; - - fd->timer->Reset(); + timer->Reset(); - while (bytes > 0 && errno != EINTR) - bytes = read(fd->input, buf, FIFO_BUFFER_SIZE); + ssize_t bytes; + do { + char buffer[16384]; + bytes = read(input, buffer, sizeof(buffer)); + } while (bytes > 0 && errno != EINTR); if (bytes < 0 && errno != EAGAIN) { FormatErrno(fifo_output_domain, "Flush of FIFO \"%s\" failed", - fd->path_utf8.c_str()); + path_utf8.c_str()); } } -static unsigned -fifo_output_delay(AudioOutput *ao) +inline unsigned +FifoOutput::Delay() const { - FifoOutput *fd = (FifoOutput *)ao; - - return fd->timer->IsStarted() - ? fd->timer->GetDelay() + return timer->IsStarted() + ? timer->GetDelay() : 0; } -static size_t -fifo_output_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +FifoOutput::Play(const void *chunk, size_t size, Error &error) { - FifoOutput *fd = (FifoOutput *)ao; - ssize_t bytes; - - if (!fd->timer->IsStarted()) - fd->timer->Start(); - fd->timer->Add(size); + if (!timer->IsStarted()) + timer->Start(); + timer->Add(size); while (true) { - bytes = write(fd->output, chunk, size); + ssize_t bytes = write(output, chunk, size); if (bytes > 0) return (size_t)bytes; @@ -275,33 +263,35 @@ fifo_output_play(AudioOutput *ao, const void *chunk, size_t size, switch (errno) { case EAGAIN: /* The pipe is full, so empty it */ - fifo_output_cancel(&fd->base); + Cancel(); continue; case EINTR: continue; } error.FormatErrno("Failed to write to FIFO %s", - fd->path_utf8.c_str()); + path_utf8.c_str()); return 0; } } } +typedef AudioOutputWrapper<FifoOutput> Wrapper; + const struct AudioOutputPlugin fifo_output_plugin = { "fifo", nullptr, - fifo_output_init, - fifo_output_finish, + &Wrapper::Init, + &Wrapper::Finish, nullptr, nullptr, - fifo_output_open, - fifo_output_close, - fifo_output_delay, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, nullptr, - fifo_output_play, + &Wrapper::Play, nullptr, - fifo_output_cancel, + &Wrapper::Cancel, nullptr, nullptr, }; diff --git a/src/output/plugins/FifoOutputPlugin.hxx b/src/output/plugins/FifoOutputPlugin.hxx index f41ceded6..353be51a6 100644 --- a/src/output/plugins/FifoOutputPlugin.hxx +++ b/src/output/plugins/FifoOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/HaikuOutputPlugin.cxx b/src/output/plugins/HaikuOutputPlugin.cxx new file mode 100644 index 000000000..e235d595a --- /dev/null +++ b/src/output/plugins/HaikuOutputPlugin.cxx @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2003-2015 The Music Player Daemon Project + * http://www.musicpd.org + * Copyright (C) 2014-2015 François 'mmu_man' Revol + * + * 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 "HaikuOutputPlugin.hxx" +#include "../OutputAPI.hxx" +#include "../Wrapper.hxx" +#include "mixer/MixerList.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" +#include "Log.hxx" + +#include <AppFileInfo.h> +#include <Application.h> +#include <Bitmap.h> +#include <IconUtils.h> +#include <MediaDefs.h> +#include <MediaRoster.h> +#include <Notification.h> +#include <OS.h> +#include <Resources.h> +#include <StringList.h> +#include <SoundPlayer.h> + +#include <string.h> + +#define UTF8_PLAY "\xE2\x96\xB6" + +class HaikuOutput { + friend struct AudioOutputWrapper<HaikuOutput>; + friend int haiku_output_get_volume(HaikuOutput &haiku); + friend bool haiku_output_set_volume(HaikuOutput &haiku, unsigned volume); + + AudioOutput base; + + size_t write_size; + + media_raw_audio_format* format; + BSoundPlayer* sound_player; + + sem_id new_buffer; + sem_id buffer_done; + + uint8* buffer; + size_t buffer_size; + size_t buffer_filled; + + unsigned buffer_delay; + +public: + HaikuOutput() + :base(haiku_output_plugin) {} + ~HaikuOutput(); + + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); + } + + static HaikuOutput *Create(const ConfigBlock &block, Error &error); + + bool Open(AudioFormat &audio_format, Error &error); + + void Close() { + DoClose(); + } + + size_t Play(const void *chunk, size_t size, Error &error); + void Cancel(); + + bool Configure(const ConfigBlock &block, Error &error); + + size_t Delay(); + + void FillBuffer(void* _buffer, size_t size, + gcc_unused const media_raw_audio_format& _format); + + void SendTag(const Tag &tag); + +private: + + void DoClose(); +}; + +static constexpr Domain haiku_output_domain("haiku_output"); + +static void +haiku_output_error(Error &error_r, status_t err) +{ + const char *error = strerror(err); + error_r.Set(haiku_output_domain, err, error); +} + +static void +initialize_application() +{ + // required to send the notification with a bitmap + // TODO: actually Run() it and handle B_QUIT_REQUESTED + // TODO: use some locking? + if (be_app == NULL) { + FormatDebug(haiku_output_domain, "creating be_app\n"); + new BApplication("application/x-vnd.MusicPD"); + } +} + +static void +finalize_application() +{ + // TODO: use some locking? + delete be_app; + be_app = NULL; + FormatDebug(haiku_output_domain, "deleting be_app\n"); +} + +inline bool +HaikuOutput::Configure(const ConfigBlock &block, Error &error) +{ + /* XXX: by default we should let the MediaKit propose the buffer size */ + write_size = block.GetBlockValue("write_size", 4096u); + + format = (media_raw_audio_format*)malloc( + sizeof(media_raw_audio_format)); + if (format == nullptr) { + haiku_output_error(error, B_NO_MEMORY); + return false; + } + + return true; +} + +static bool +haiku_test_default_device(void) +{ + BSoundPlayer testPlayer; + return testPlayer.InitCheck() == B_OK; + +} + +inline HaikuOutput * +HaikuOutput::Create(const ConfigBlock &block, Error &error) +{ + initialize_application(); + + HaikuOutput *ad = new HaikuOutput(); + + if (!ad->Initialize(block, error)) { + delete ad; + return nullptr; + } + + if (!ad->Configure(block, error)) { + delete ad; + return nullptr; + } + + return ad; +} + +void +HaikuOutput::DoClose() +{ + sound_player->SetHasData(false); + delete_sem(new_buffer); + delete_sem(buffer_done); + sound_player->Stop(); + delete sound_player; + sound_player = nullptr; +} + + + +HaikuOutput::~HaikuOutput() +{ + free(format); + delete_sem(new_buffer); + delete_sem(buffer_done); + + finalize_application(); +} + +static void +fill_buffer(void* cookie, void* buffer, size_t size, + const media_raw_audio_format& format) +{ + HaikuOutput *ad = (HaikuOutput *)cookie; + ad->FillBuffer(buffer, size, format); +} + + +void +HaikuOutput::FillBuffer(void* _buffer, size_t size, + gcc_unused const media_raw_audio_format& _format) +{ + + buffer = (uint8*)_buffer; + buffer_size = size; + buffer_filled = 0; + bigtime_t start = system_time(); + release_sem(new_buffer); + acquire_sem(buffer_done); + bigtime_t w = system_time() - start; + + if (w > 5000LL) { + FormatDebug(haiku_output_domain, + "haiku:fill_buffer waited %Ldus\n", w); + } + + if (buffer_filled < buffer_size) { + memset(buffer + buffer_filled, 0, + buffer_size - buffer_filled); + FormatDebug(haiku_output_domain, + "haiku:fill_buffer filled %d size %d clearing remainder\n", + (int)buffer_filled, (int)buffer_size); + + } +} + +inline bool +HaikuOutput::Open(AudioFormat &audio_format, Error &error) +{ + status_t err; + *format = media_multi_audio_format::wildcard; + + switch (audio_format.format) { + case SampleFormat::S8: + format->format = media_raw_audio_format::B_AUDIO_CHAR; + break; + + case SampleFormat::S16: + format->format = media_raw_audio_format::B_AUDIO_SHORT; + break; + + case SampleFormat::S32: + format->format = media_raw_audio_format::B_AUDIO_INT; + break; + + case SampleFormat::FLOAT: + format->format = media_raw_audio_format::B_AUDIO_FLOAT; + break; + + default: + /* fall back to float */ + audio_format.format = SampleFormat::FLOAT; + format->format = media_raw_audio_format::B_AUDIO_FLOAT; + break; + } + + format->frame_rate = audio_format.sample_rate; + format->byte_order = B_MEDIA_HOST_ENDIAN; + format->channel_count = audio_format.channels; + + buffer_size = 0; + + if (write_size) + format->buffer_size = write_size; + else + format->buffer_size = BMediaRoster::Roster()->AudioBufferSizeFor( + format->channel_count, format->format, + format->frame_rate, B_UNKNOWN_BUS) * 2; + + FormatDebug(haiku_output_domain, + "using haiku driver ad: bs: %d ws: %d " + "channels %d rate %f fmt %08lx bs %d\n", + (int)buffer_size, (int)write_size, + (int)format->channel_count, format->frame_rate, + format->format, (int)format->buffer_size); + + sound_player = new BSoundPlayer(format, "MPD Output", + fill_buffer, NULL, this); + + err = sound_player->InitCheck(); + if (err != B_OK) { + delete sound_player; + sound_player = NULL; + haiku_output_error(error, err); + return false; + } + + // calculate the allowable delay for the buffer (ms) + buffer_delay = format->buffer_size; + buffer_delay /= (format->format & + media_raw_audio_format::B_AUDIO_SIZE_MASK); + buffer_delay /= format->channel_count; + buffer_delay *= 1000 / format->frame_rate; + // half of the total buffer play time + buffer_delay /= 2; + FormatDebug(haiku_output_domain, + "buffer delay: %d ms\n", buffer_delay); + + new_buffer = create_sem(0, "New buffer request"); + buffer_done = create_sem(0, "Buffer done"); + + sound_player->SetVolume(1.0); + sound_player->Start(); + sound_player->SetHasData(false); + + return true; +} + +inline size_t +HaikuOutput::Play(const void *chunk, size_t size, gcc_unused Error &error) +{ + BSoundPlayer* const soundPlayer = sound_player; + const uint8 *data = (const uint8 *)chunk; + + if (size == 0) { + soundPlayer->SetHasData(false); + return 0; + } + + if (!soundPlayer->HasData()) + soundPlayer->SetHasData(true); + acquire_sem(new_buffer); + + size_t bytesLeft = size; + while (bytesLeft > 0) { + if (buffer_filled == buffer_size) { + // Request another buffer from BSoundPlayer + release_sem(buffer_done); + acquire_sem(new_buffer); + } + + const size_t copyBytes = std::min(bytesLeft, buffer_size + - buffer_filled); + memcpy(buffer + buffer_filled, data, + copyBytes); + buffer_filled += copyBytes; + data += copyBytes; + bytesLeft -= copyBytes; + } + + + if (buffer_filled < buffer_size) { + // Continue filling this buffer the next time this function is called + release_sem(new_buffer); + } else { + // Buffer is full + release_sem(buffer_done); + //soundPlayer->SetHasData(false); + } + + return size; +} + +inline size_t +HaikuOutput::Delay() +{ + unsigned delay = buffer_filled ? 0 : buffer_delay; + + //FormatDebug(haiku_output_domain, + // "delay=%d\n", delay / 2); + // XXX: doesn't work + //return (delay / 2) ? 1 : 0; + (void)delay; + + return 0; +} + +inline void +HaikuOutput::SendTag(const Tag &tag) +{ + status_t err; + + /* lazily initialized */ + static BBitmap *icon = NULL; + + if (icon == NULL) { + BAppFileInfo info; + BResources resources; + err = resources.SetToImage((const void *)&HaikuOutput::SendTag); + BFile file(resources.File()); + err = info.SetTo(&file); + icon = new BBitmap(BRect(0, 0, (float)B_LARGE_ICON - 1, + (float)B_LARGE_ICON - 1), B_BITMAP_NO_SERVER_LINK, B_RGBA32); + err = info.GetIcon(icon, B_LARGE_ICON); + if (err != B_OK) { + delete icon; + icon = NULL; + } + } + + BNotification notification(B_INFORMATION_NOTIFICATION); + + BString messageId("mpd_"); + messageId << find_thread(NULL); + notification.SetMessageID(messageId); + + notification.SetGroup("Music Player Daemon"); + + char timebuf[16]; + unsigned seconds = 0; + if (!tag.duration.IsNegative()) { + seconds = tag.duration.ToS(); + snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", + seconds / 3600, (seconds % 3600) / 60, seconds % 60); + } + + BString artist; + BString album; + BString title; + BString track; + BString name; + + for (const auto &item : tag) + { + switch (item.type) { + case TAG_ARTIST: + case TAG_ALBUM_ARTIST: + if (artist.Length() == 0) + artist << item.value; + break; + case TAG_ALBUM: + if (album.Length() == 0) + album << item.value; + break; + case TAG_TITLE: + if (title.Length() == 0) + title << item.value; + break; + case TAG_TRACK: + if (track.Length() == 0) + track << item.value; + break; + case TAG_NAME: + if (name.Length() == 0) + name << item.value; + break; + case TAG_GENRE: + case TAG_DATE: + case TAG_PERFORMER: + case TAG_COMMENT: + case TAG_DISC: + case TAG_COMPOSER: + case TAG_MUSICBRAINZ_ARTISTID: + case TAG_MUSICBRAINZ_ALBUMID: + case TAG_MUSICBRAINZ_ALBUMARTISTID: + case TAG_MUSICBRAINZ_TRACKID: + default: + FormatDebug(haiku_output_domain, + "tag item: type %d value '%s'\n", item.type, item.value); + break; + } + } + + notification.SetTitle(UTF8_PLAY " Now Playing:"); + + BStringList content; + if (name.Length()) + content.Add(name); + if (artist.Length()) + content.Add(artist); + if (album.Length()) + content.Add(album); + if (track.Length()) + content.Add(track); + if (title.Length()) + content.Add(title); + + if (content.CountStrings() == 0) + content.Add("(Unknown)"); + + BString full = content.Join(" " B_UTF8_BULLET " "); + + if (seconds > 0) + full << " (" << timebuf << ")"; + + notification.SetContent(full); + + err = notification.SetIcon(icon); + + notification.Send(); +} + +int +haiku_output_get_volume(HaikuOutput &haiku) +{ + BSoundPlayer* const soundPlayer = haiku.sound_player; + + if (soundPlayer == NULL || soundPlayer->InitCheck() != B_OK) + return 0; + + return (int)(soundPlayer->Volume() * 100 + 0.5); +} + +bool +haiku_output_set_volume(HaikuOutput &haiku, unsigned volume) +{ + BSoundPlayer* const soundPlayer = haiku.sound_player; + + if (soundPlayer == NULL || soundPlayer->InitCheck() != B_OK) + return false; + + soundPlayer->SetVolume((float)volume / 100); + return true; +} + +typedef AudioOutputWrapper<HaikuOutput> Wrapper; + +const struct AudioOutputPlugin haiku_output_plugin = { + "haiku", + haiku_test_default_device, + &Wrapper::Init, + &Wrapper::Finish, + nullptr, + nullptr, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, + &Wrapper::SendTag, + &Wrapper::Play, + nullptr, + nullptr, + nullptr, + + &haiku_mixer_plugin, +}; diff --git a/src/output/plugins/HaikuOutputPlugin.hxx b/src/output/plugins/HaikuOutputPlugin.hxx new file mode 100644 index 000000000..fb85d4b83 --- /dev/null +++ b/src/output/plugins/HaikuOutputPlugin.hxx @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2003-2015 The Music Player Daemon Project + * http://www.musicpd.org + * Copyright (C) 2014-2015 François 'mmu_man' Revol + * + * 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_HAIKU_OUTPUT_PLUGIN_HXX +#define MPD_HAIKU_OUTPUT_PLUGIN_HXX + +class HaikuOutput; + +extern const struct AudioOutputPlugin haiku_output_plugin; + +int +haiku_output_get_volume(HaikuOutput &haiku); + +bool +haiku_output_set_volume(HaikuOutput &haiku, unsigned volume); + +#endif diff --git a/src/output/plugins/JackOutputPlugin.cxx b/src/output/plugins/JackOutputPlugin.cxx index e1dad7893..23843ab5e 100644 --- a/src/output/plugins/JackOutputPlugin.cxx +++ b/src/output/plugins/JackOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,26 +20,27 @@ #include "config.h" #include "JackOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "config/ConfigError.hxx" +#include "util/ConstBuffer.hxx" +#include "util/SplitString.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" #include <assert.h> -#include <glib.h> #include <jack/jack.h> #include <jack/types.h> #include <jack/ringbuffer.h> +#include <unistd.h> /* for usleep() */ #include <stdlib.h> #include <string.h> -enum { - MAX_PORTS = 16, -}; +static constexpr unsigned MAX_PORTS = 16; -static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t); +static constexpr size_t jack_sample_size = sizeof(jack_default_audio_sample_t); struct JackOutput { AudioOutput base; @@ -55,10 +56,10 @@ struct JackOutput { /* configuration */ - char *source_ports[MAX_PORTS]; + std::string source_ports[MAX_PORTS]; unsigned num_source_ports; - char *destination_ports[MAX_PORTS]; + std::string destination_ports[MAX_PORTS]; unsigned num_destination_ports; size_t ringbuffer_size; @@ -82,24 +83,65 @@ struct JackOutput { JackOutput() :base(jack_output_plugin) {} - bool Initialize(const config_param ¶m, Error &error_r) { - return base.Configure(param, error_r); + bool Configure(const ConfigBlock &block, Error &error); + + bool Connect(Error &error); + + /** + * Disconnect the JACK client. + */ + void Disconnect(); + + void Shutdown() { + shutdown = true; + } + + bool Enable(Error &error); + void Disable(); + + bool Open(AudioFormat &new_audio_format, Error &error); + + void Close() { + Stop(); + } + + bool Start(Error &error); + void Stop(); + + /** + * Determine the number of frames guaranteed to be available + * on all channels. + */ + gcc_pure + jack_nframes_t GetAvailable() const; + + void Process(jack_nframes_t nframes); + + /** + * @return the number of frames that were written + */ + size_t WriteSamples(const float *src, size_t n_frames); + + unsigned Delay() const { + return base.pause && pause && !shutdown + ? 1000 + : 0; } + + size_t Play(const void *chunk, size_t size, Error &error); + + bool Pause(); }; static constexpr Domain jack_output_domain("jack_output"); -/** - * Determine the number of frames guaranteed to be available on all - * channels. - */ -static jack_nframes_t -mpd_jack_available(const JackOutput *jd) +inline jack_nframes_t +JackOutput::GetAvailable() const { - size_t min = jack_ringbuffer_read_space(jd->ringbuffer[0]); + size_t min = jack_ringbuffer_read_space(ringbuffer[0]); - for (unsigned i = 1; i < jd->audio_format.channels; ++i) { - size_t current = jack_ringbuffer_read_space(jd->ringbuffer[i]); + for (unsigned i = 1; i < audio_format.channels; ++i) { + size_t current = jack_ringbuffer_read_space(ringbuffer[i]); if (current < min) min = current; } @@ -109,85 +151,121 @@ mpd_jack_available(const JackOutput *jd) return min / jack_sample_size; } -static int -mpd_jack_process(jack_nframes_t nframes, void *arg) +/** + * Call jack_ringbuffer_read_advance() on all buffers in the list. + */ +static void +MultiReadAdvance(ConstBuffer<jack_ringbuffer_t *> buffers, + size_t size) { - JackOutput *jd = (JackOutput *) arg; + for (auto *i : buffers) + jack_ringbuffer_read_advance(i, size); +} +/** + * Write a specific amount of "silence" to the given port. + */ +static void +WriteSilence(jack_port_t &port, jack_nframes_t nframes) +{ + jack_default_audio_sample_t *out = + (jack_default_audio_sample_t *) + jack_port_get_buffer(&port, nframes); + if (out == nullptr) + /* workaround for libjack1 bug: if the server + connection fails, the process callback is invoked + anyway, but unable to get a buffer */ + return; + + std::fill_n(out, nframes, 0.0); +} + +/** + * Write a specific amount of "silence" to all ports in the list. + */ +static void +MultiWriteSilence(ConstBuffer<jack_port_t *> ports, jack_nframes_t nframes) +{ + for (auto *i : ports) + WriteSilence(*i, nframes); +} + +/** + * Copy data from the buffer to the port. If the buffer underruns, + * fill with silence. + */ +static void +Copy(jack_port_t &dest, jack_nframes_t nframes, + jack_ringbuffer_t &src, jack_nframes_t available) +{ + jack_default_audio_sample_t *out = + (jack_default_audio_sample_t *) + jack_port_get_buffer(&dest, nframes); + if (out == nullptr) + /* workaround for libjack1 bug: if the server + connection fails, the process callback is + invoked anyway, but unable to get a + buffer */ + return; + + /* copy from buffer to port */ + jack_ringbuffer_read(&src, (char *)out, + available * jack_sample_size); + + /* ringbuffer underrun, fill with silence */ + std::fill(out + available, out + nframes, 0.0); +} + +inline void +JackOutput::Process(jack_nframes_t nframes) +{ if (nframes <= 0) - return 0; + return; - if (jd->pause) { + jack_nframes_t available = GetAvailable(); + + const unsigned n_channels = audio_format.channels; + + if (pause) { /* empty the ring buffers */ - const jack_nframes_t available = mpd_jack_available(jd); - for (unsigned i = 0; i < jd->audio_format.channels; ++i) - jack_ringbuffer_read_advance(jd->ringbuffer[i], - available * jack_sample_size); + MultiReadAdvance({ringbuffer, n_channels}, + available * jack_sample_size); /* generate silence while MPD is paused */ - for (unsigned i = 0; i < jd->audio_format.channels; ++i) { - jack_default_audio_sample_t *out = - (jack_default_audio_sample_t *) - jack_port_get_buffer(jd->ports[i], nframes); + MultiWriteSilence({ports, n_channels}, nframes); - for (jack_nframes_t f = 0; f < nframes; ++f) - out[f] = 0.0; - } - - return 0; + return; } - jack_nframes_t available = mpd_jack_available(jd); if (available > nframes) available = nframes; - for (unsigned i = 0; i < jd->audio_format.channels; ++i) { - jack_default_audio_sample_t *out = - (jack_default_audio_sample_t *) - jack_port_get_buffer(jd->ports[i], nframes); - if (out == nullptr) - /* workaround for libjack1 bug: if the server - connection fails, the process callback is - invoked anyway, but unable to get a - buffer */ - continue; - - jack_ringbuffer_read(jd->ringbuffer[i], - (char *)out, available * jack_sample_size); - - for (jack_nframes_t f = available; f < nframes; ++f) - /* ringbuffer underrun, fill with silence */ - out[f] = 0.0; - } + for (unsigned i = 0; i < n_channels; ++i) + Copy(*ports[i], nframes, *ringbuffer[i], available); /* generate silence for the unused source ports */ - for (unsigned i = jd->audio_format.channels; - i < jd->num_source_ports; ++i) { - jack_default_audio_sample_t *out = - (jack_default_audio_sample_t *) - jack_port_get_buffer(jd->ports[i], nframes); - if (out == nullptr) - /* workaround for libjack1 bug: if the server - connection fails, the process callback is - invoked anyway, but unable to get a - buffer */ - continue; - - for (jack_nframes_t f = 0; f < nframes; ++f) - out[f] = 0.0; - } + MultiWriteSilence({ports + n_channels, num_source_ports - n_channels}, + nframes); +} + +static int +mpd_jack_process(jack_nframes_t nframes, void *arg) +{ + JackOutput &jo = *(JackOutput *) arg; + jo.Process(nframes); return 0; } static void mpd_jack_shutdown(void *arg) { - JackOutput *jd = (JackOutput *) arg; - jd->shutdown = true; + JackOutput &jo = *(JackOutput *) arg; + + jo.Shutdown(); } static void @@ -200,9 +278,10 @@ set_audioformat(JackOutput *jd, AudioFormat &audio_format) else if (audio_format.channels > jd->num_source_ports) audio_format.channels = 2; - if (audio_format.format != SampleFormat::S16 && - audio_format.format != SampleFormat::S24_P32) - audio_format.format = SampleFormat::S24_P32; + /* JACK uses 32 bit float in the range [-1 .. 1] - just like + MPD's SampleFormat::FLOAT*/ + static_assert(jack_sample_size == sizeof(float), "Expected float32"); + audio_format.format = SampleFormat::FLOAT; } static void @@ -219,55 +298,47 @@ mpd_jack_info(const char *msg) } #endif -/** - * Disconnect the JACK client. - */ -static void -mpd_jack_disconnect(JackOutput *jd) +void +JackOutput::Disconnect() { - assert(jd != nullptr); - assert(jd->client != nullptr); + assert(client != nullptr); - jack_deactivate(jd->client); - jack_client_close(jd->client); - jd->client = nullptr; + jack_deactivate(client); + jack_client_close(client); + client = nullptr; } /** * Connect the JACK client and performs some basic setup * (e.g. register callbacks). */ -static bool -mpd_jack_connect(JackOutput *jd, Error &error) +bool +JackOutput::Connect(Error &error) { - jack_status_t status; - - assert(jd != nullptr); - - jd->shutdown = false; + shutdown = false; - jd->client = jack_client_open(jd->name, jd->options, &status, - jd->server_name); - if (jd->client == nullptr) { + jack_status_t status; + client = jack_client_open(name, options, &status, server_name); + if (client == nullptr) { error.Format(jack_output_domain, status, "Failed to connect to JACK server, status=%d", status); return false; } - jack_set_process_callback(jd->client, mpd_jack_process, jd); - jack_on_shutdown(jd->client, mpd_jack_shutdown, jd); + jack_set_process_callback(client, mpd_jack_process, this); + jack_on_shutdown(client, mpd_jack_shutdown, this); - for (unsigned i = 0; i < jd->num_source_ports; ++i) { - jd->ports[i] = jack_port_register(jd->client, - jd->source_ports[i], - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, 0); - if (jd->ports[i] == nullptr) { + for (unsigned i = 0; i < num_source_ports; ++i) { + ports[i] = jack_port_register(client, + source_ports[i].c_str(), + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (ports[i] == nullptr) { error.Format(jack_output_domain, "Cannot register output port \"%s\"", - jd->source_ports[i]); - mpd_jack_disconnect(jd); + source_ports[i].c_str()); + Disconnect(); return false; } } @@ -282,23 +353,19 @@ mpd_jack_test_default_device(void) } static unsigned -parse_port_list(const char *source, char **dest, Error &error) +parse_port_list(const char *source, std::string dest[], Error &error) { - char **list = g_strsplit(source, ",", 0); unsigned n = 0; - - for (n = 0; list[n] != nullptr; ++n) { + for (auto &&i : SplitString(source, ',')) { if (n >= MAX_PORTS) { error.Set(config_domain, "too many port names"); return 0; } - dest[n] = list[n]; + dest[n++] = std::move(i); } - g_free(list); - if (n == 0) { error.Format(config_domain, "at least one port name expected"); @@ -308,243 +375,221 @@ parse_port_list(const char *source, char **dest, Error &error) return n; } -static AudioOutput * -mpd_jack_init(const config_param ¶m, Error &error) +bool +JackOutput::Configure(const ConfigBlock &block, Error &error) { - JackOutput *jd = new JackOutput(); - - if (!jd->Initialize(param, error)) { - delete jd; - return nullptr; - } - - const char *value; + if (!base.Configure(block, error)) + return false; - jd->options = JackNullOption; + options = JackNullOption; - jd->name = param.GetBlockValue("client_name", nullptr); - if (jd->name != nullptr) - jd->options = jack_options_t(jd->options | JackUseExactName); + name = block.GetBlockValue("client_name", nullptr); + if (name != nullptr) + options = jack_options_t(options | JackUseExactName); else /* if there's a no configured client name, we don't care about the JackUseExactName option */ - jd->name = "Music Player Daemon"; + name = "Music Player Daemon"; - jd->server_name = param.GetBlockValue("server_name", nullptr); - if (jd->server_name != nullptr) - jd->options = jack_options_t(jd->options | JackServerName); + server_name = block.GetBlockValue("server_name", nullptr); + if (server_name != nullptr) + options = jack_options_t(options | JackServerName); - if (!param.GetBlockValue("autostart", false)) - jd->options = jack_options_t(jd->options | JackNoStartServer); + if (!block.GetBlockValue("autostart", false)) + options = jack_options_t(options | JackNoStartServer); /* configure the source ports */ - value = param.GetBlockValue("source_ports", "left,right"); - jd->num_source_ports = parse_port_list(value, - jd->source_ports, error); - if (jd->num_source_ports == 0) - return nullptr; + const char *value = block.GetBlockValue("source_ports", "left,right"); + num_source_ports = parse_port_list(value, source_ports, error); + if (num_source_ports == 0) + return false; /* configure the destination ports */ - value = param.GetBlockValue("destination_ports", nullptr); + value = block.GetBlockValue("destination_ports", nullptr); if (value == nullptr) { /* compatibility with MPD < 0.16 */ - value = param.GetBlockValue("ports", nullptr); + value = block.GetBlockValue("ports", nullptr); if (value != nullptr) FormatWarning(jack_output_domain, "deprecated option 'ports' in line %d", - param.line); + block.line); } if (value != nullptr) { - jd->num_destination_ports = - parse_port_list(value, - jd->destination_ports, error); - if (jd->num_destination_ports == 0) - return nullptr; + num_destination_ports = + parse_port_list(value, destination_ports, error); + if (num_destination_ports == 0) + return false; } else { - jd->num_destination_ports = 0; + num_destination_ports = 0; } - if (jd->num_destination_ports > 0 && - jd->num_destination_ports != jd->num_source_ports) + if (num_destination_ports > 0 && + num_destination_ports != num_source_ports) FormatWarning(jack_output_domain, "number of source ports (%u) mismatches the " "number of destination ports (%u) in line %d", - jd->num_source_ports, jd->num_destination_ports, - param.line); - - jd->ringbuffer_size = param.GetBlockValue("ringbuffer_size", 32768u); + num_source_ports, num_destination_ports, + block.line); - jack_set_error_function(mpd_jack_error); + ringbuffer_size = block.GetBlockValue("ringbuffer_size", 32768u); -#ifdef HAVE_JACK_SET_INFO_FUNCTION - jack_set_info_function(mpd_jack_info); -#endif - - return &jd->base; + return true; } -static void -mpd_jack_finish(AudioOutput *ao) +inline bool +JackOutput::Enable(Error &error) { - JackOutput *jd = (JackOutput *)ao; - - for (unsigned i = 0; i < jd->num_source_ports; ++i) - g_free(jd->source_ports[i]); + for (unsigned i = 0; i < num_source_ports; ++i) + ringbuffer[i] = nullptr; - for (unsigned i = 0; i < jd->num_destination_ports; ++i) - g_free(jd->destination_ports[i]); - - delete jd; + return Connect(error); } -static bool -mpd_jack_enable(AudioOutput *ao, Error &error) +inline void +JackOutput::Disable() { - JackOutput *jd = (JackOutput *)ao; + if (client != nullptr) + Disconnect(); - for (unsigned i = 0; i < jd->num_source_ports; ++i) - jd->ringbuffer[i] = nullptr; - - return mpd_jack_connect(jd, error); + for (unsigned i = 0; i < num_source_ports; ++i) { + if (ringbuffer[i] != nullptr) { + jack_ringbuffer_free(ringbuffer[i]); + ringbuffer[i] = nullptr; + } + } } -static void -mpd_jack_disable(AudioOutput *ao) +static AudioOutput * +mpd_jack_init(const ConfigBlock &block, Error &error) { - JackOutput *jd = (JackOutput *)ao; - - if (jd->client != nullptr) - mpd_jack_disconnect(jd); + JackOutput *jd = new JackOutput(); - for (unsigned i = 0; i < jd->num_source_ports; ++i) { - if (jd->ringbuffer[i] != nullptr) { - jack_ringbuffer_free(jd->ringbuffer[i]); - jd->ringbuffer[i] = nullptr; - } + if (!jd->Configure(block, error)) { + delete jd; + return nullptr; } + + jack_set_error_function(mpd_jack_error); + +#ifdef HAVE_JACK_SET_INFO_FUNCTION + jack_set_info_function(mpd_jack_info); +#endif + + return &jd->base; } /** * Stops the playback on the JACK connection. */ -static void -mpd_jack_stop(JackOutput *jd) +void +JackOutput::Stop() { - assert(jd != nullptr); - - if (jd->client == nullptr) + if (client == nullptr) return; - if (jd->shutdown) + if (shutdown) /* the connection has failed; close it */ - mpd_jack_disconnect(jd); + Disconnect(); else /* the connection is alive: just stop playback */ - jack_deactivate(jd->client); + jack_deactivate(client); } -static bool -mpd_jack_start(JackOutput *jd, Error &error) +inline bool +JackOutput::Start(Error &error) { - const char *destination_ports[MAX_PORTS], **jports; - const char *duplicate_port = nullptr; - unsigned num_destination_ports; - - assert(jd->client != nullptr); - assert(jd->audio_format.channels <= jd->num_source_ports); + assert(client != nullptr); + assert(audio_format.channels <= num_source_ports); /* allocate the ring buffers on the first open(); these persist until MPD exits. It's too unsafe to delete them because we can never know when mpd_jack_process() gets called */ - for (unsigned i = 0; i < jd->num_source_ports; ++i) { - if (jd->ringbuffer[i] == nullptr) - jd->ringbuffer[i] = - jack_ringbuffer_create(jd->ringbuffer_size); + for (unsigned i = 0; i < num_source_ports; ++i) { + if (ringbuffer[i] == nullptr) + ringbuffer[i] = + jack_ringbuffer_create(ringbuffer_size); /* clear the ring buffer to be sure that data from previous playbacks are gone */ - jack_ringbuffer_reset(jd->ringbuffer[i]); + jack_ringbuffer_reset(ringbuffer[i]); } - if ( jack_activate(jd->client) ) { + if ( jack_activate(client) ) { error.Set(jack_output_domain, "cannot activate client"); - mpd_jack_stop(jd); + Stop(); return false; } - if (jd->num_destination_ports == 0) { + const char *dports[MAX_PORTS], **jports; + unsigned num_dports; + if (num_destination_ports == 0) { /* no output ports were configured - ask libjack for defaults */ - jports = jack_get_ports(jd->client, nullptr, nullptr, + jports = jack_get_ports(client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput); if (jports == nullptr) { error.Set(jack_output_domain, "no ports found"); - mpd_jack_stop(jd); + Stop(); return false; } assert(*jports != nullptr); - for (num_destination_ports = 0; - num_destination_ports < MAX_PORTS && - jports[num_destination_ports] != nullptr; - ++num_destination_ports) { + for (num_dports = 0; num_dports < MAX_PORTS && + jports[num_dports] != nullptr; + ++num_dports) { FormatDebug(jack_output_domain, "destination_port[%u] = '%s'\n", - num_destination_ports, - jports[num_destination_ports]); - destination_ports[num_destination_ports] = - jports[num_destination_ports]; + num_dports, + jports[num_dports]); + dports[num_dports] = jports[num_dports]; } } else { /* use the configured output ports */ - num_destination_ports = jd->num_destination_ports; - memcpy(destination_ports, jd->destination_ports, - num_destination_ports * sizeof(*destination_ports)); + num_dports = num_destination_ports; + for (unsigned i = 0; i < num_dports; ++i) + dports[i] = destination_ports[i].c_str(); jports = nullptr; } - assert(num_destination_ports > 0); + assert(num_dports > 0); - if (jd->audio_format.channels >= 2 && num_destination_ports == 1) { + const char *duplicate_port = nullptr; + if (audio_format.channels >= 2 && num_dports == 1) { /* mix stereo signal on one speaker */ - while (num_destination_ports < jd->audio_format.channels) - destination_ports[num_destination_ports++] = - destination_ports[0]; - } else if (num_destination_ports > jd->audio_format.channels) { - if (jd->audio_format.channels == 1 && num_destination_ports > 2) { + std::fill(dports + num_dports, dports + audio_format.channels, + dports[0]); + } else if (num_dports > audio_format.channels) { + if (audio_format.channels == 1 && num_dports > 2) { /* mono input file: connect the one source channel to the both destination channels */ - duplicate_port = destination_ports[1]; - num_destination_ports = 1; + duplicate_port = dports[1]; + num_dports = 1; } else /* connect only as many ports as we need */ - num_destination_ports = jd->audio_format.channels; + num_dports = audio_format.channels; } - assert(num_destination_ports <= jd->num_source_ports); - - for (unsigned i = 0; i < num_destination_ports; ++i) { - int ret; + assert(num_dports <= num_source_ports); - ret = jack_connect(jd->client, jack_port_name(jd->ports[i]), - destination_ports[i]); + for (unsigned i = 0; i < num_dports; ++i) { + int ret = jack_connect(client, jack_port_name(ports[i]), + dports[i]); if (ret != 0) { error.Format(jack_output_domain, - "Not a valid JACK port: %s", - destination_ports[i]); + "Not a valid JACK port: %s", dports[i]); if (jports != nullptr) free(jports); - mpd_jack_stop(jd); + Stop(); return false; } } @@ -554,7 +599,7 @@ mpd_jack_start(JackOutput *jd, Error &error) the both destination channels */ int ret; - ret = jack_connect(jd->client, jack_port_name(jd->ports[0]), + ret = jack_connect(client, jack_port_name(ports[0]), duplicate_port); if (ret != 0) { error.Format(jack_output_domain, @@ -564,7 +609,7 @@ mpd_jack_start(JackOutput *jd, Error &error) if (jports != nullptr) free(jports); - mpd_jack_stop(jd); + Stop(); return false; } } @@ -575,188 +620,119 @@ mpd_jack_start(JackOutput *jd, Error &error) return true; } -static bool -mpd_jack_open(AudioOutput *ao, AudioFormat &audio_format, - Error &error) +inline bool +JackOutput::Open(AudioFormat &new_audio_format, Error &error) { - JackOutput *jd = (JackOutput *)ao; + pause = false; - assert(jd != nullptr); + if (client != nullptr && shutdown) + Disconnect(); - jd->pause = false; - - if (jd->client != nullptr && jd->shutdown) - mpd_jack_disconnect(jd); - - if (jd->client == nullptr && !mpd_jack_connect(jd, error)) + if (client == nullptr && !Connect(error)) return false; - set_audioformat(jd, audio_format); - jd->audio_format = audio_format; + set_audioformat(this, new_audio_format); + audio_format = new_audio_format; - if (!mpd_jack_start(jd, error)) - return false; - - return true; + return Start(error); } -static void -mpd_jack_close(gcc_unused AudioOutput *ao) +inline size_t +JackOutput::WriteSamples(const float *src, size_t n_frames) { - JackOutput *jd = (JackOutput *)ao; + assert(n_frames > 0); - mpd_jack_stop(jd); -} + const unsigned n_channels = audio_format.channels; -static unsigned -mpd_jack_delay(AudioOutput *ao) -{ - JackOutput *jd = (JackOutput *)ao; + float *dest[MAX_CHANNELS]; + size_t space = -1; + for (unsigned i = 0; i < n_channels; ++i) { + jack_ringbuffer_data_t d[2]; + jack_ringbuffer_get_write_vector(ringbuffer[i], d); - return jd->base.pause && jd->pause && !jd->shutdown - ? 1000 - : 0; -} + /* choose the first non-empty writable area */ + const jack_ringbuffer_data_t &e = d[d[0].len == 0]; -static inline jack_default_audio_sample_t -sample_16_to_jack(int16_t sample) -{ - return sample / (jack_default_audio_sample_t)(1 << (16 - 1)); -} + if (e.len < space) + /* send data symmetrically */ + space = e.len; -static void -mpd_jack_write_samples_16(JackOutput *jd, const int16_t *src, - unsigned num_samples) -{ - jack_default_audio_sample_t sample; - unsigned i; - - while (num_samples-- > 0) { - for (i = 0; i < jd->audio_format.channels; ++i) { - sample = sample_16_to_jack(*src++); - jack_ringbuffer_write(jd->ringbuffer[i], - (const char *)&sample, - sizeof(sample)); - } + dest[i] = (float *)e.buf; } -} -static inline jack_default_audio_sample_t -sample_24_to_jack(int32_t sample) -{ - return sample / (jack_default_audio_sample_t)(1 << (24 - 1)); -} + space /= jack_sample_size; + if (space == 0) + return 0; -static void -mpd_jack_write_samples_24(JackOutput *jd, const int32_t *src, - unsigned num_samples) -{ - jack_default_audio_sample_t sample; - unsigned i; - - while (num_samples-- > 0) { - for (i = 0; i < jd->audio_format.channels; ++i) { - sample = sample_24_to_jack(*src++); - jack_ringbuffer_write(jd->ringbuffer[i], - (const char *)&sample, - sizeof(sample)); - } - } -} + const size_t result = n_frames = std::min(space, n_frames); -static void -mpd_jack_write_samples(JackOutput *jd, const void *src, - unsigned num_samples) -{ - switch (jd->audio_format.format) { - case SampleFormat::S16: - mpd_jack_write_samples_16(jd, (const int16_t*)src, - num_samples); - break; - - case SampleFormat::S24_P32: - mpd_jack_write_samples_24(jd, (const int32_t*)src, - num_samples); - break; - - default: - assert(false); - gcc_unreachable(); - } + while (n_frames-- > 0) + for (unsigned i = 0; i < n_channels; ++i) + *dest[i]++ = *src++; + + const size_t per_channel_advance = result * jack_sample_size; + for (unsigned i = 0; i < n_channels; ++i) + jack_ringbuffer_write_advance(ringbuffer[i], + per_channel_advance); + + return result; } -static size_t -mpd_jack_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +JackOutput::Play(const void *chunk, size_t size, Error &error) { - JackOutput *jd = (JackOutput *)ao; - const size_t frame_size = jd->audio_format.GetFrameSize(); - size_t space = 0, space1; - - jd->pause = false; + pause = false; + const size_t frame_size = audio_format.GetFrameSize(); assert(size % frame_size == 0); size /= frame_size; while (true) { - if (jd->shutdown) { + if (shutdown) { error.Set(jack_output_domain, "Refusing to play, because " "there is no client thread"); return 0; } - space = jack_ringbuffer_write_space(jd->ringbuffer[0]); - for (unsigned i = 1; i < jd->audio_format.channels; ++i) { - space1 = jack_ringbuffer_write_space(jd->ringbuffer[i]); - if (space > space1) - /* send data symmetrically */ - space = space1; - } - - if (space >= jack_sample_size) - break; + size_t frames_written = + WriteSamples((const float *)chunk, size); + if (frames_written > 0) + return frames_written * frame_size; /* XXX do something more intelligent to synchronize */ - g_usleep(1000); + usleep(1000); } - - space /= jack_sample_size; - if (space < size) - size = space; - - mpd_jack_write_samples(jd, chunk, size); - return size * frame_size; } -static bool -mpd_jack_pause(AudioOutput *ao) +inline bool +JackOutput::Pause() { - JackOutput *jd = (JackOutput *)ao; - - if (jd->shutdown) + if (shutdown) return false; - jd->pause = true; + pause = true; return true; } +typedef AudioOutputWrapper<JackOutput> Wrapper; + const struct AudioOutputPlugin jack_output_plugin = { "jack", mpd_jack_test_default_device, mpd_jack_init, - mpd_jack_finish, - mpd_jack_enable, - mpd_jack_disable, - mpd_jack_open, - mpd_jack_close, - mpd_jack_delay, + &Wrapper::Finish, + &Wrapper::Enable, + &Wrapper::Disable, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, nullptr, - mpd_jack_play, + &Wrapper::Play, nullptr, nullptr, - mpd_jack_pause, + &Wrapper::Pause, nullptr, }; diff --git a/src/output/plugins/JackOutputPlugin.hxx b/src/output/plugins/JackOutputPlugin.hxx index 6f1f7ecb9..f76431690 100644 --- a/src/output/plugins/JackOutputPlugin.hxx +++ b/src/output/plugins/JackOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/NullOutputPlugin.cxx b/src/output/plugins/NullOutputPlugin.cxx index 098f58926..e1731f0fe 100644 --- a/src/output/plugins/NullOutputPlugin.cxx +++ b/src/output/plugins/NullOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,119 +20,94 @@ #include "config.h" #include "NullOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "../Timer.hxx" -struct NullOutput { +class NullOutput { + friend struct AudioOutputWrapper<NullOutput>; + AudioOutput base; bool sync; Timer *timer; +public: NullOutput() :base(null_output_plugin) {} - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); - } -}; - -static AudioOutput * -null_init(const config_param ¶m, Error &error) -{ - NullOutput *nd = new NullOutput(); - - if (!nd->Initialize(param, error)) { - delete nd; - return nullptr; + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); } - nd->sync = param.GetBlockValue("sync", true); + static NullOutput *Create(const ConfigBlock &block, Error &error); - return &nd->base; -} + bool Open(AudioFormat &audio_format, gcc_unused Error &error) { + if (sync) + timer = new Timer(audio_format); -static void -null_finish(AudioOutput *ao) -{ - NullOutput *nd = (NullOutput *)ao; - - delete nd; -} - -static bool -null_open(AudioOutput *ao, AudioFormat &audio_format, - gcc_unused Error &error) -{ - NullOutput *nd = (NullOutput *)ao; - - if (nd->sync) - nd->timer = new Timer(audio_format); + return true; + } - return true; -} + void Close() { + if (sync) + delete timer; + } -static void -null_close(AudioOutput *ao) -{ - NullOutput *nd = (NullOutput *)ao; + unsigned Delay() const { + return sync && timer->IsStarted() + ? timer->GetDelay() + : 0; + } - if (nd->sync) - delete nd->timer; -} + size_t Play(gcc_unused const void *chunk, size_t size, + gcc_unused Error &error) { + if (sync) { + if (!timer->IsStarted()) + timer->Start(); + timer->Add(size); + } -static unsigned -null_delay(AudioOutput *ao) -{ - NullOutput *nd = (NullOutput *)ao; + return size; + } - return nd->sync && nd->timer->IsStarted() - ? nd->timer->GetDelay() - : 0; -} + void Cancel() { + if (sync) + timer->Reset(); + } +}; -static size_t -null_play(AudioOutput *ao, gcc_unused const void *chunk, size_t size, - gcc_unused Error &error) +inline NullOutput * +NullOutput::Create(const ConfigBlock &block, Error &error) { - NullOutput *nd = (NullOutput *)ao; - Timer *timer = nd->timer; + NullOutput *nd = new NullOutput(); - if (!nd->sync) - return size; + if (!nd->Initialize(block, error)) { + delete nd; + return nullptr; + } - if (!timer->IsStarted()) - timer->Start(); - timer->Add(size); + nd->sync = block.GetBlockValue("sync", true); - return size; + return nd; } -static void -null_cancel(AudioOutput *ao) -{ - NullOutput *nd = (NullOutput *)ao; - - if (!nd->sync) - return; - - nd->timer->Reset(); -} +typedef AudioOutputWrapper<NullOutput> Wrapper; const struct AudioOutputPlugin null_output_plugin = { "null", nullptr, - null_init, - null_finish, + &Wrapper::Init, + &Wrapper::Finish, nullptr, nullptr, - null_open, - null_close, - null_delay, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, nullptr, - null_play, + &Wrapper::Play, nullptr, - null_cancel, + &Wrapper::Cancel, nullptr, nullptr, }; diff --git a/src/output/plugins/NullOutputPlugin.hxx b/src/output/plugins/NullOutputPlugin.hxx index f25f5b9f3..9a1d1558b 100644 --- a/src/output/plugins/NullOutputPlugin.hxx +++ b/src/output/plugins/NullOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/OSXOutputPlugin.cxx b/src/output/plugins/OSXOutputPlugin.cxx index 13ac7b35e..16c042ba3 100644 --- a/src/output/plugins/OSXOutputPlugin.cxx +++ b/src/output/plugins/OSXOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -61,17 +61,17 @@ osx_output_test_default_device(void) } static void -osx_output_configure(OSXOutput *oo, const config_param ¶m) +osx_output_configure(OSXOutput *oo, const ConfigBlock &block) { - const char *device = param.GetBlockValue("device"); + const char *device = block.GetBlockValue("device"); - if (device == NULL || 0 == strcmp(device, "default")) { + if (device == nullptr || 0 == strcmp(device, "default")) { oo->component_subtype = kAudioUnitSubType_DefaultOutput; - oo->device_name = NULL; + oo->device_name = nullptr; } else if (0 == strcmp(device, "system")) { oo->component_subtype = kAudioUnitSubType_SystemOutput; - oo->device_name = NULL; + oo->device_name = nullptr; } else { oo->component_subtype = kAudioUnitSubType_HALOutput; @@ -81,15 +81,15 @@ osx_output_configure(OSXOutput *oo, const config_param ¶m) } static AudioOutput * -osx_output_init(const config_param ¶m, Error &error) +osx_output_init(const ConfigBlock &block, Error &error) { OSXOutput *oo = new OSXOutput(); - if (!oo->base.Configure(param, error)) { + if (!oo->base.Configure(block, error)) { delete oo; - return NULL; + return nullptr; } - osx_output_configure(oo, param); + osx_output_configure(oo, block); return &oo->base; } @@ -108,7 +108,7 @@ osx_output_set_device(OSXOutput *oo, Error &error) bool ret = true; OSStatus status; UInt32 size, numdevices; - AudioDeviceID *deviceids = NULL; + AudioDeviceID *deviceids = nullptr; char name[256]; unsigned int i; @@ -118,7 +118,7 @@ osx_output_set_device(OSXOutput *oo, Error &error) /* how many audio devices are there? */ status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &size, - NULL); + nullptr); if (status != noErr) { error.Format(osx_output_domain, status, "Unable to determine number of OS X audio devices: %s", @@ -206,7 +206,7 @@ osx_render(void *vdata, AudioBuffer *buffer = &buffer_list->mBuffers[0]; size_t buffer_size = buffer->mDataByteSize; - assert(od->buffer != NULL); + assert(od->buffer != nullptr); od->mutex.lock(); @@ -245,7 +245,7 @@ osx_output_enable(AudioOutput *ao, Error &error) desc.componentFlags = 0; desc.componentFlagsMask = 0; - Component comp = FindNextComponent(NULL, &desc); + Component comp = FindNextComponent(nullptr, &desc); if (comp == 0) { error.Set(osx_output_domain, "Error finding OS X component"); diff --git a/src/output/plugins/OSXOutputPlugin.hxx b/src/output/plugins/OSXOutputPlugin.hxx index d7aed40b6..89cd3fe91 100644 --- a/src/output/plugins/OSXOutputPlugin.hxx +++ b/src/output/plugins/OSXOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/OpenALOutputPlugin.cxx b/src/output/plugins/OpenALOutputPlugin.cxx index 2f095c0a4..eb55c6e9b 100644 --- a/src/output/plugins/OpenALOutputPlugin.cxx +++ b/src/output/plugins/OpenALOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "OpenALOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -33,10 +34,12 @@ #include <OpenAL/alc.h> #endif -/* should be enough for buffer size = 2048 */ -#define NUM_BUFFERS 16 +class OpenALOutput { + friend struct AudioOutputWrapper<OpenALOutput>; + + /* should be enough for buffer size = 2048 */ + static constexpr unsigned NUM_BUFFERS = 16; -struct OpenALOutput { AudioOutput base; const char *device_name; @@ -51,9 +54,47 @@ struct OpenALOutput { OpenALOutput() :base(openal_output_plugin) {} - bool Initialize(const config_param ¶m, Error &error_r) { - return base.Configure(param, error_r); + bool Configure(const ConfigBlock &block, Error &error); + + static OpenALOutput *Create(const ConfigBlock &block, Error &error); + + bool Open(AudioFormat &audio_format, Error &error); + + void Close(); + + gcc_pure + unsigned Delay() const { + return filled < NUM_BUFFERS || HasProcessed() + ? 0 + /* we don't know exactly how long we must wait + for the next buffer to finish, so this is a + random guess: */ + : 50; + } + + size_t Play(const void *chunk, size_t size, Error &error); + + void Cancel(); + +private: + gcc_pure + ALint GetSourceI(ALenum param) const { + ALint value; + alGetSourcei(source, param, &value); + return value; + } + + gcc_pure + bool HasProcessed() const { + return GetSourceI(AL_BUFFERS_PROCESSED) > 0; } + + gcc_pure + bool IsPlaying() const { + return GetSourceI(AL_SOURCE_STATE) == AL_PLAYING; + } + + bool SetupContext(Error &error); }; static constexpr Domain openal_output_domain("openal_output"); @@ -83,200 +124,154 @@ openal_audio_format(AudioFormat &audio_format) } } -gcc_pure -static inline ALint -openal_get_source_i(const OpenALOutput *od, ALenum param) -{ - ALint value; - alGetSourcei(od->source, param, &value); - return value; -} - -gcc_pure -static inline bool -openal_has_processed(const OpenALOutput *od) -{ - return openal_get_source_i(od, AL_BUFFERS_PROCESSED) > 0; -} - -gcc_pure -static inline ALint -openal_is_playing(const OpenALOutput *od) -{ - return openal_get_source_i(od, AL_SOURCE_STATE) == AL_PLAYING; -} - -static bool -openal_setup_context(OpenALOutput *od, Error &error) +inline bool +OpenALOutput::SetupContext(Error &error) { - od->device = alcOpenDevice(od->device_name); + device = alcOpenDevice(device_name); - if (od->device == nullptr) { + if (device == nullptr) { error.Format(openal_output_domain, "Error opening OpenAL device \"%s\"", - od->device_name); + device_name); return false; } - od->context = alcCreateContext(od->device, nullptr); + context = alcCreateContext(device, nullptr); - if (od->context == nullptr) { + if (context == nullptr) { error.Format(openal_output_domain, "Error creating context for \"%s\"", - od->device_name); - alcCloseDevice(od->device); + device_name); + alcCloseDevice(device); return false; } return true; } -static AudioOutput * -openal_init(const config_param ¶m, Error &error) +inline bool +OpenALOutput::Configure(const ConfigBlock &block, Error &error) { - const char *device_name = param.GetBlockValue("device"); - if (device_name == nullptr) { - device_name = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER); - } - - OpenALOutput *od = new OpenALOutput(); - if (!od->Initialize(param, error)) { - delete od; - return nullptr; - } + if (!base.Configure(block, error)) + return false; - od->device_name = device_name; + device_name = block.GetBlockValue("device"); + if (device_name == nullptr) + device_name = alcGetString(nullptr, + ALC_DEFAULT_DEVICE_SPECIFIER); - return &od->base; + return true; } -static void -openal_finish(AudioOutput *ao) +inline OpenALOutput * +OpenALOutput::Create(const ConfigBlock &block, Error &error) { - OpenALOutput *od = (OpenALOutput *)ao; + OpenALOutput *oo = new OpenALOutput(); + + if (!oo->Configure(block, error)) { + delete oo; + return nullptr; + } - delete od; + return oo; } -static bool -openal_open(AudioOutput *ao, AudioFormat &audio_format, - Error &error) +inline bool +OpenALOutput::Open(AudioFormat &audio_format, Error &error) { - OpenALOutput *od = (OpenALOutput *)ao; + format = openal_audio_format(audio_format); - od->format = openal_audio_format(audio_format); - - if (!openal_setup_context(od, error)) { + if (!SetupContext(error)) return false; - } - alcMakeContextCurrent(od->context); - alGenBuffers(NUM_BUFFERS, od->buffers); + alcMakeContextCurrent(context); + alGenBuffers(NUM_BUFFERS, buffers); if (alGetError() != AL_NO_ERROR) { error.Set(openal_output_domain, "Failed to generate buffers"); return false; } - alGenSources(1, &od->source); + alGenSources(1, &source); if (alGetError() != AL_NO_ERROR) { error.Set(openal_output_domain, "Failed to generate source"); - alDeleteBuffers(NUM_BUFFERS, od->buffers); + alDeleteBuffers(NUM_BUFFERS, buffers); return false; } - od->filled = 0; - od->frequency = audio_format.sample_rate; + filled = 0; + frequency = audio_format.sample_rate; return true; } -static void -openal_close(AudioOutput *ao) +inline void +OpenALOutput::Close() { - OpenALOutput *od = (OpenALOutput *)ao; - - alcMakeContextCurrent(od->context); - alDeleteSources(1, &od->source); - alDeleteBuffers(NUM_BUFFERS, od->buffers); - alcDestroyContext(od->context); - alcCloseDevice(od->device); + alcMakeContextCurrent(context); + alDeleteSources(1, &source); + alDeleteBuffers(NUM_BUFFERS, buffers); + alcDestroyContext(context); + alcCloseDevice(device); } -static unsigned -openal_delay(AudioOutput *ao) +inline size_t +OpenALOutput::Play(const void *chunk, size_t size, gcc_unused Error &error) { - OpenALOutput *od = (OpenALOutput *)ao; - - return od->filled < NUM_BUFFERS || openal_has_processed(od) - ? 0 - /* we don't know exactly how long we must wait for the - next buffer to finish, so this is a random - guess: */ - : 50; -} + if (alcGetCurrentContext() != context) + alcMakeContextCurrent(context); -static size_t -openal_play(AudioOutput *ao, const void *chunk, size_t size, - gcc_unused Error &error) -{ - OpenALOutput *od = (OpenALOutput *)ao; ALuint buffer; - - if (alcGetCurrentContext() != od->context) { - alcMakeContextCurrent(od->context); - } - - if (od->filled < NUM_BUFFERS) { + if (filled < NUM_BUFFERS) { /* fill all buffers */ - buffer = od->buffers[od->filled]; - od->filled++; + buffer = buffers[filled]; + filled++; } else { /* wait for processed buffer */ - while (!openal_has_processed(od)) + while (!HasProcessed()) usleep(10); - alSourceUnqueueBuffers(od->source, 1, &buffer); + alSourceUnqueueBuffers(source, 1, &buffer); } - alBufferData(buffer, od->format, chunk, size, od->frequency); - alSourceQueueBuffers(od->source, 1, &buffer); + alBufferData(buffer, format, chunk, size, frequency); + alSourceQueueBuffers(source, 1, &buffer); - if (!openal_is_playing(od)) - alSourcePlay(od->source); + if (!IsPlaying()) + alSourcePlay(source); return size; } -static void -openal_cancel(AudioOutput *ao) +inline void +OpenALOutput::Cancel() { - OpenALOutput *od = (OpenALOutput *)ao; - - od->filled = 0; - alcMakeContextCurrent(od->context); - alSourceStop(od->source); + filled = 0; + alcMakeContextCurrent(context); + alSourceStop(source); /* force-unqueue all buffers */ - alSourcei(od->source, AL_BUFFER, 0); - od->filled = 0; + alSourcei(source, AL_BUFFER, 0); + filled = 0; } +typedef AudioOutputWrapper<OpenALOutput> Wrapper; + const struct AudioOutputPlugin openal_output_plugin = { "openal", nullptr, - openal_init, - openal_finish, + &Wrapper::Init, + &Wrapper::Finish, nullptr, nullptr, - openal_open, - openal_close, - openal_delay, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, nullptr, - openal_play, + &Wrapper::Play, nullptr, - openal_cancel, + &Wrapper::Cancel, nullptr, nullptr, }; diff --git a/src/output/plugins/OpenALOutputPlugin.hxx b/src/output/plugins/OpenALOutputPlugin.hxx index a27e6b53c..abf4ffdb1 100644 --- a/src/output/plugins/OpenALOutputPlugin.hxx +++ b/src/output/plugins/OpenALOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx index 39d87fc35..7f75f4e31 100644 --- a/src/output/plugins/OssOutputPlugin.cxx +++ b/src/output/plugins/OssOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "OssOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "system/fd_util.h" #include "util/ConstBuffer.hxx" @@ -57,7 +58,9 @@ #include "util/Manual.hxx" #endif -struct OssOutput { +class OssOutput { + friend struct AudioOutputWrapper<OssOutput>; + AudioOutput base; #ifdef AFMT_S24_PACKED @@ -79,13 +82,49 @@ struct OssOutput { */ int oss_format; - OssOutput() +public: + OssOutput(const char *_device=nullptr) :base(oss_output_plugin), - fd(-1), device(nullptr) {} + fd(-1), device(_device) {} - bool Initialize(const config_param ¶m, Error &error_r) { - return base.Configure(param, error_r); + bool Initialize(const ConfigBlock &block, Error &error_r) { + return base.Configure(block, error_r); } + + static OssOutput *Create(const ConfigBlock &block, Error &error); + +#ifdef AFMT_S24_PACKED + bool Enable(gcc_unused Error &error) { + pcm_export.Construct(); + return true; + } + + void Disable() { + pcm_export.Destruct(); + } +#endif + + bool Open(AudioFormat &audio_format, Error &error); + + void Close() { + DoClose(); + } + + size_t Play(const void *chunk, size_t size, Error &error); + void Cancel(); + +private: + /** + * Sets up the OSS device which was opened before. + */ + bool Setup(AudioFormat &audio_format, Error &error); + + /** + * Reopen the device with the saved audio_format, without any probing. + */ + bool Reopen(Error &error); + + void DoClose(); }; static constexpr Domain oss_output_domain("oss_output"); @@ -124,7 +163,7 @@ oss_stat_device(const char *device, int *errno_r) return OSS_STAT_NO_ERROR; } -static const char *default_devices[] = { "/dev/sound/dsp", "/dev/dsp" }; +static const char *const default_devices[] = { "/dev/sound/dsp", "/dev/dsp" }; static bool oss_output_test_default_device(void) @@ -147,24 +186,23 @@ oss_output_test_default_device(void) return false; } -static AudioOutput * +static OssOutput * oss_open_default(Error &error) { int err[ARRAY_SIZE(default_devices)]; enum oss_stat ret[ARRAY_SIZE(default_devices)]; - const config_param empty; + const ConfigBlock empty; for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) { ret[i] = oss_stat_device(default_devices[i], &err[i]); if (ret[i] == OSS_STAT_NO_ERROR) { - OssOutput *od = new OssOutput(); + OssOutput *od = new OssOutput(default_devices[i]); if (!od->Initialize(empty, error)) { delete od; - return NULL; + return nullptr; } - od->device = default_devices[i]; - return &od->base; + return od; } } @@ -194,62 +232,33 @@ oss_open_default(Error &error) error.Set(oss_output_domain, "error trying to open default OSS device"); - return NULL; + return nullptr; } -static AudioOutput * -oss_output_init(const config_param ¶m, Error &error) +inline OssOutput * +OssOutput::Create(const ConfigBlock &block, Error &error) { - const char *device = param.GetBlockValue("device"); - if (device != NULL) { + const char *device = block.GetBlockValue("device"); + if (device != nullptr) { OssOutput *od = new OssOutput(); - if (!od->Initialize(param, error)) { + if (!od->Initialize(block, error)) { delete od; - return NULL; + return nullptr; } od->device = device; - return &od->base; + return od; } return oss_open_default(error); } -static void -oss_output_finish(AudioOutput *ao) +void +OssOutput::DoClose() { - OssOutput *od = (OssOutput *)ao; - - delete od; -} - -#ifdef AFMT_S24_PACKED - -static bool -oss_output_enable(AudioOutput *ao, gcc_unused Error &error) -{ - OssOutput *od = (OssOutput *)ao; - - od->pcm_export.Construct(); - return true; -} - -static void -oss_output_disable(AudioOutput *ao) -{ - OssOutput *od = (OssOutput *)ao; - - od->pcm_export.Destruct(); -} - -#endif - -static void -oss_close(OssOutput *od) -{ - if (od->fd >= 0) - close(od->fd); - od->fd = -1; + if (fd >= 0) + close(fd); + fd = -1; } /** @@ -271,8 +280,8 @@ oss_try_ioctl_r(int fd, unsigned long request, int *value_r, const char *msg, Error &error) { assert(fd >= 0); - assert(value_r != NULL); - assert(msg != NULL); + assert(value_r != nullptr); + assert(msg != nullptr); assert(!error.IsDefined()); int ret = ioctl(fd, request, value_r); @@ -380,7 +389,7 @@ oss_setup_sample_rate(int fd, AudioFormat &audio_format, break; } - static const int sample_rates[] = { 48000, 44100, 0 }; + static constexpr int sample_rates[] = { 48000, 44100, 0 }; for (unsigned i = 0; sample_rates[i] != 0; ++i) { sample_rate = sample_rates[i]; if (sample_rate == (int)audio_format.sample_rate) @@ -412,6 +421,7 @@ oss_setup_sample_rate(int fd, AudioFormat &audio_format, * Convert a MPD sample format to its OSS counterpart. Returns * AFMT_QUERY if there is no direct counterpart. */ +gcc_const static int sample_format_to_oss(SampleFormat format) { @@ -442,13 +452,15 @@ sample_format_to_oss(SampleFormat format) #endif } - return AFMT_QUERY; + assert(false); + gcc_unreachable(); } /** * Convert an OSS sample format to its MPD counterpart. Returns * SampleFormat::UNDEFINED if there is no direct counterpart. */ +gcc_const static SampleFormat sample_format_from_oss(int format) { @@ -572,7 +584,7 @@ oss_setup_sample_format(int fd, AudioFormat &audio_format, /* the requested sample format is not available - probe for other formats supported by MPD */ - static const SampleFormat sample_formats[] = { + static constexpr SampleFormat sample_formats[] = { SampleFormat::S24_P32, SampleFormat::S32, SampleFormat::S16, @@ -609,18 +621,14 @@ oss_setup_sample_format(int fd, AudioFormat &audio_format, return false; } -/** - * Sets up the OSS device which was opened before. - */ -static bool -oss_setup(OssOutput *od, AudioFormat &audio_format, - Error &error) +inline bool +OssOutput::Setup(AudioFormat &_audio_format, Error &error) { - return oss_setup_channels(od->fd, audio_format, error) && - oss_setup_sample_rate(od->fd, audio_format, error) && - oss_setup_sample_format(od->fd, audio_format, &od->oss_format, + return oss_setup_channels(fd, _audio_format, error) && + oss_setup_sample_rate(fd, _audio_format, error) && + oss_setup_sample_format(fd, _audio_format, &oss_format, #ifdef AFMT_S24_PACKED - od->pcm_export, + pcm_export, #endif error); } @@ -628,46 +636,46 @@ oss_setup(OssOutput *od, AudioFormat &audio_format, /** * Reopen the device with the saved audio_format, without any probing. */ -static bool -oss_reopen(OssOutput *od, Error &error) +inline bool +OssOutput::Reopen(Error &error) { - assert(od->fd < 0); + assert(fd < 0); - od->fd = open_cloexec(od->device, O_WRONLY, 0); - if (od->fd < 0) { + fd = open_cloexec(device, O_WRONLY, 0); + if (fd < 0) { error.FormatErrno("Error opening OSS device \"%s\"", - od->device); + device); return false; } enum oss_setup_result result; const char *const msg1 = "Failed to set channel count"; - result = oss_try_ioctl(od->fd, SNDCTL_DSP_CHANNELS, - od->audio_format.channels, msg1, error); + result = oss_try_ioctl(fd, SNDCTL_DSP_CHANNELS, + audio_format.channels, msg1, error); if (result != SUCCESS) { - oss_close(od); + DoClose(); if (result == UNSUPPORTED) error.Set(oss_output_domain, msg1); return false; } const char *const msg2 = "Failed to set sample rate"; - result = oss_try_ioctl(od->fd, SNDCTL_DSP_SPEED, - od->audio_format.sample_rate, msg2, error); + result = oss_try_ioctl(fd, SNDCTL_DSP_SPEED, + audio_format.sample_rate, msg2, error); if (result != SUCCESS) { - oss_close(od); + DoClose(); if (result == UNSUPPORTED) error.Set(oss_output_domain, msg2); return false; } const char *const msg3 = "Failed to set sample format"; - result = oss_try_ioctl(od->fd, SNDCTL_DSP_SAMPLESIZE, - od->oss_format, + result = oss_try_ioctl(fd, SNDCTL_DSP_SAMPLESIZE, + oss_format, msg3, error); if (result != SUCCESS) { - oss_close(od); + DoClose(); if (result == UNSUPPORTED) error.Set(oss_output_domain, msg3); return false; @@ -676,62 +684,47 @@ oss_reopen(OssOutput *od, Error &error) return true; } -static bool -oss_output_open(AudioOutput *ao, AudioFormat &audio_format, - Error &error) +inline bool +OssOutput::Open(AudioFormat &_audio_format, Error &error) { - OssOutput *od = (OssOutput *)ao; - - od->fd = open_cloexec(od->device, O_WRONLY, 0); - if (od->fd < 0) { + fd = open_cloexec(device, O_WRONLY, 0); + if (fd < 0) { error.FormatErrno("Error opening OSS device \"%s\"", - od->device); + device); return false; } - if (!oss_setup(od, audio_format, error)) { - oss_close(od); + if (!Setup(_audio_format, error)) { + DoClose(); return false; } - od->audio_format = audio_format; + audio_format = _audio_format; return true; } -static void -oss_output_close(AudioOutput *ao) +inline void +OssOutput::Cancel() { - OssOutput *od = (OssOutput *)ao; - - oss_close(od); -} - -static void -oss_output_cancel(AudioOutput *ao) -{ - OssOutput *od = (OssOutput *)ao; - - if (od->fd >= 0) { - ioctl(od->fd, SNDCTL_DSP_RESET, 0); - oss_close(od); + if (fd >= 0) { + ioctl(fd, SNDCTL_DSP_RESET, 0); + DoClose(); } } -static size_t -oss_output_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +OssOutput::Play(const void *chunk, size_t size, Error &error) { - OssOutput *od = (OssOutput *)ao; ssize_t ret; assert(size > 0); /* reopen the device since it was closed by dropBufferedAudio */ - if (od->fd < 0 && !oss_reopen(od, error)) + if (fd < 0 && !Reopen(error)) return 0; #ifdef AFMT_S24_PACKED - const auto e = od->pcm_export->Export({chunk, size}); + const auto e = pcm_export->Export({chunk, size}); chunk = e.data; size = e.size; #endif @@ -739,40 +732,42 @@ oss_output_play(AudioOutput *ao, const void *chunk, size_t size, assert(size > 0); while (true) { - ret = write(od->fd, chunk, size); + ret = write(fd, chunk, size); if (ret > 0) { #ifdef AFMT_S24_PACKED - ret = od->pcm_export->CalcSourceSize(ret); + ret = pcm_export->CalcSourceSize(ret); #endif return ret; } if (ret < 0 && errno != EINTR) { - error.FormatErrno("Write error on %s", od->device); + error.FormatErrno("Write error on %s", device); return 0; } } } +typedef AudioOutputWrapper<OssOutput> Wrapper; + const struct AudioOutputPlugin oss_output_plugin = { "oss", oss_output_test_default_device, - oss_output_init, - oss_output_finish, + &Wrapper::Init, + &Wrapper::Finish, #ifdef AFMT_S24_PACKED - oss_output_enable, - oss_output_disable, + &Wrapper::Enable, + &Wrapper::Disable, #else nullptr, nullptr, #endif - oss_output_open, - oss_output_close, + &Wrapper::Open, + &Wrapper::Close, nullptr, nullptr, - oss_output_play, + &Wrapper::Play, nullptr, - oss_output_cancel, + &Wrapper::Cancel, nullptr, &oss_mixer_plugin, diff --git a/src/output/plugins/OssOutputPlugin.hxx b/src/output/plugins/OssOutputPlugin.hxx index f9970c8f0..8f9b3d424 100644 --- a/src/output/plugins/OssOutputPlugin.hxx +++ b/src/output/plugins/OssOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/PipeOutputPlugin.cxx b/src/output/plugins/PipeOutputPlugin.cxx index d8075d505..c70e2d498 100644 --- a/src/output/plugins/PipeOutputPlugin.cxx +++ b/src/output/plugins/PipeOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "PipeOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "config/ConfigError.hxx" #include "util/Error.hxx" @@ -27,7 +28,9 @@ #include <stdio.h> -struct PipeOutput { +class PipeOutput { + friend struct AudioOutputWrapper<PipeOutput>; + AudioOutput base; std::string cmd; @@ -36,17 +39,27 @@ struct PipeOutput { PipeOutput() :base(pipe_output_plugin) {} - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); + bool Configure(const ConfigBlock &block, Error &error); + +public: + static PipeOutput *Create(const ConfigBlock &block, Error &error); + + bool Open(AudioFormat &audio_format, Error &error); + + void Close() { + pclose(fh); } - bool Configure(const config_param ¶m, Error &error); + size_t Play(const void *chunk, size_t size, Error &error); }; inline bool -PipeOutput::Configure(const config_param ¶m, Error &error) +PipeOutput::Configure(const ConfigBlock &block, Error &error) { - cmd = param.GetBlockValue("command", ""); + if (!base.Configure(block, error)) + return false; + + cmd = block.GetBlockValue("command", ""); if (cmd.empty()) { error.Set(config_domain, "No \"command\" parameter specified"); @@ -56,83 +69,56 @@ PipeOutput::Configure(const config_param ¶m, Error &error) return true; } -static AudioOutput * -pipe_output_init(const config_param ¶m, Error &error) +inline PipeOutput * +PipeOutput::Create(const ConfigBlock &block, Error &error) { - PipeOutput *pd = new PipeOutput(); + PipeOutput *po = new PipeOutput(); - if (!pd->Initialize(param, error)) { - delete pd; + if (!po->Configure(block, error)) { + delete po; return nullptr; } - if (!pd->Configure(param, error)) { - delete pd; - return nullptr; - } - - return &pd->base; + return po; } -static void -pipe_output_finish(AudioOutput *ao) -{ - PipeOutput *pd = (PipeOutput *)ao; - - delete pd; -} - -static bool -pipe_output_open(AudioOutput *ao, - gcc_unused AudioFormat &audio_format, - Error &error) +inline bool +PipeOutput::Open(gcc_unused AudioFormat &audio_format, Error &error) { - PipeOutput *pd = (PipeOutput *)ao; - - pd->fh = popen(pd->cmd.c_str(), "w"); - if (pd->fh == nullptr) { + fh = popen(cmd.c_str(), "w"); + if (fh == nullptr) { error.FormatErrno("Error opening pipe \"%s\"", - pd->cmd.c_str()); + cmd.c_str()); return false; } return true; } -static void -pipe_output_close(AudioOutput *ao) -{ - PipeOutput *pd = (PipeOutput *)ao; - - pclose(pd->fh); -} - -static size_t -pipe_output_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +PipeOutput::Play(const void *chunk, size_t size, Error &error) { - PipeOutput *pd = (PipeOutput *)ao; - size_t ret; - - ret = fwrite(chunk, 1, size, pd->fh); - if (ret == 0) + size_t nbytes = fwrite(chunk, 1, size, fh); + if (nbytes == 0) error.SetErrno("Write error on pipe"); - return ret; + return nbytes; } +typedef AudioOutputWrapper<PipeOutput> Wrapper; + const struct AudioOutputPlugin pipe_output_plugin = { "pipe", nullptr, - pipe_output_init, - pipe_output_finish, + &Wrapper::Init, + &Wrapper::Finish, nullptr, nullptr, - pipe_output_open, - pipe_output_close, + &Wrapper::Open, + &Wrapper::Close, nullptr, nullptr, - pipe_output_play, + &Wrapper::Play, nullptr, nullptr, nullptr, diff --git a/src/output/plugins/PipeOutputPlugin.hxx b/src/output/plugins/PipeOutputPlugin.hxx index bdaf2ecfd..5d92848c2 100644 --- a/src/output/plugins/PipeOutputPlugin.hxx +++ b/src/output/plugins/PipeOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/PulseOutputPlugin.cxx b/src/output/plugins/PulseOutputPlugin.cxx index 120bad090..8b5225584 100644 --- a/src/output/plugins/PulseOutputPlugin.cxx +++ b/src/output/plugins/PulseOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,11 +19,14 @@ #include "config.h" #include "PulseOutputPlugin.hxx" +#include "lib/pulse/Domain.hxx" +#include "lib/pulse/Error.hxx" +#include "lib/pulse/LogError.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx" #include "util/Error.hxx" -#include "util/Domain.hxx" #include "Log.hxx" #include <pulse/thread-mainloop.h> @@ -31,7 +34,6 @@ #include <pulse/stream.h> #include <pulse/introspect.h> #include <pulse/subscribe.h> -#include <pulse/error.h> #include <pulse/version.h> #include <assert.h> @@ -40,7 +42,9 @@ #define MPD_PULSE_NAME "Music Player Daemon" -struct PulseOutput { +class PulseOutput { + friend struct AudioOutputWrapper<PulseOutput>; + AudioOutput base; const char *name; @@ -56,80 +60,190 @@ struct PulseOutput { size_t writable; PulseOutput() - :base(pulse_output_plugin) {} -}; + :base(pulse_output_plugin), + mixer(nullptr), + mainloop(nullptr), stream(nullptr) {} -static constexpr Domain pulse_output_domain("pulse_output"); +public: + void SetMixer(PulseMixer &_mixer); -static void -SetError(Error &error, pa_context *context, const char *msg) -{ - const int e = pa_context_errno(context); - error.Format(pulse_output_domain, e, "%s: %s", msg, pa_strerror(e)); -} + void ClearMixer(gcc_unused PulseMixer &old_mixer) { + assert(mixer == &old_mixer); + + mixer = nullptr; + } + + bool SetVolume(const pa_cvolume &volume, Error &error); + + void Lock() { + pa_threaded_mainloop_lock(mainloop); + } + + void Unlock() { + pa_threaded_mainloop_unlock(mainloop); + } + + void OnContextStateChanged(pa_context_state_t new_state); + void OnServerLayoutChanged(pa_subscription_event_type_t t, + uint32_t idx); + void OnStreamSuspended(pa_stream *_stream); + void OnStreamStateChanged(pa_stream *_stream, + pa_stream_state_t new_state); + void OnStreamWrite(size_t nbytes); + + void OnStreamSuccess() { + Signal(); + } + + gcc_const + static bool TestDefaultDevice(); + + bool Configure(const ConfigBlock &block, Error &error); + static PulseOutput *Create(const ConfigBlock &block, Error &error); + + bool Enable(Error &error); + void Disable(); + + bool Open(AudioFormat &audio_format, Error &error); + void Close(); + + unsigned Delay(); + size_t Play(const void *chunk, size_t size, Error &error); + void Cancel(); + bool Pause(); + +private: + /** + * Attempt to connect asynchronously to the PulseAudio server. + * + * @return true on success, false on error + */ + bool Connect(Error &error); + + /** + * Create, set up and connect a context. + * + * Caller must lock the main loop. + * + * @return true on success, false on error + */ + bool SetupContext(Error &error); + + /** + * Frees and clears the context. + * + * Caller must lock the main loop. + */ + void DeleteContext(); + + void Signal() { + pa_threaded_mainloop_signal(mainloop, 0); + } + + /** + * Check if the context is (already) connected, and waits if + * not. If the context has been disconnected, retry to + * connect. + * + * Caller must lock the main loop. + * + * @return true on success, false on error + */ + bool WaitConnection(Error &error); + + /** + * Create, set up and connect a context. + * + * Caller must lock the main loop. + * + * @return true on success, false on error + */ + bool SetupStream(const pa_sample_spec &ss, Error &error); + + /** + * Frees and clears the stream. + */ + void DeleteStream(); + + /** + * Check if the stream is (already) connected, and waits if + * not. The mainloop must be locked before calling this + * function. + * + * @return true on success, false on error + */ + bool WaitStream(Error &error); + + /** + * Sets cork mode on the stream. + */ + bool StreamPause(bool pause, Error &error); +}; void pulse_output_lock(PulseOutput &po) { - pa_threaded_mainloop_lock(po.mainloop); + po.Lock(); } void pulse_output_unlock(PulseOutput &po) { - pa_threaded_mainloop_unlock(po.mainloop); + po.Unlock(); } -void -pulse_output_set_mixer(PulseOutput &po, PulseMixer &pm) +inline void +PulseOutput::SetMixer(PulseMixer &_mixer) { - assert(po.mixer == nullptr); + assert(mixer == nullptr); - po.mixer = ± + mixer = &_mixer; - if (po.mainloop == nullptr) + if (mainloop == nullptr) return; - pa_threaded_mainloop_lock(po.mainloop); + pa_threaded_mainloop_lock(mainloop); - if (po.context != nullptr && - pa_context_get_state(po.context) == PA_CONTEXT_READY) { - pulse_mixer_on_connect(pm, po.context); + if (context != nullptr && + pa_context_get_state(context) == PA_CONTEXT_READY) { + pulse_mixer_on_connect(_mixer, context); - if (po.stream != nullptr && - pa_stream_get_state(po.stream) == PA_STREAM_READY) - pulse_mixer_on_change(pm, po.context, po.stream); + if (stream != nullptr && + pa_stream_get_state(stream) == PA_STREAM_READY) + pulse_mixer_on_change(_mixer, context, stream); } - pa_threaded_mainloop_unlock(po.mainloop); + pa_threaded_mainloop_unlock(mainloop); } void -pulse_output_clear_mixer(PulseOutput &po, gcc_unused PulseMixer &pm) +pulse_output_set_mixer(PulseOutput &po, PulseMixer &pm) { - assert(po.mixer == &pm); - - po.mixer = nullptr; + po.SetMixer(pm); } -bool -pulse_output_set_volume(PulseOutput &po, const pa_cvolume *volume, - Error &error) +void +pulse_output_clear_mixer(PulseOutput &po, PulseMixer &pm) { - pa_operation *o; + po.ClearMixer(pm); +} - if (po.context == nullptr || po.stream == nullptr || - pa_stream_get_state(po.stream) != PA_STREAM_READY) { - error.Set(pulse_output_domain, "disconnected"); +inline bool +PulseOutput::SetVolume(const pa_cvolume &volume, Error &error) +{ + if (context == nullptr || stream == nullptr || + pa_stream_get_state(stream) != PA_STREAM_READY) { + error.Set(pulse_domain, "disconnected"); return false; } - o = pa_context_set_sink_input_volume(po.context, - pa_stream_get_index(po.stream), - volume, nullptr, nullptr); + pa_operation *o = + pa_context_set_sink_input_volume(context, + pa_stream_get_index(stream), + &volume, nullptr, nullptr); if (o == nullptr) { - SetError(error, po.context, - "failed to set PulseAudio volume"); + SetPulseError(error, context, + "failed to set PulseAudio volume"); return false; } @@ -137,6 +251,13 @@ pulse_output_set_volume(PulseOutput &po, const pa_cvolume *volume, return true; } +bool +pulse_output_set_volume(PulseOutput &po, const pa_cvolume *volume, + Error &error) +{ + return po.SetVolume(*volume, error); +} + /** * \brief waits for a pulseaudio operation to finish, frees it and * unlocks the mainloop @@ -169,32 +290,30 @@ static void pulse_output_stream_success_cb(gcc_unused pa_stream *s, gcc_unused int success, void *userdata) { - PulseOutput *po = (PulseOutput *)userdata; + PulseOutput &po = *(PulseOutput *)userdata; - pa_threaded_mainloop_signal(po->mainloop, 0); + po.OnStreamSuccess(); } -static void -pulse_output_context_state_cb(struct pa_context *context, void *userdata) +inline void +PulseOutput::OnContextStateChanged(pa_context_state_t new_state) { - PulseOutput *po = (PulseOutput *)userdata; - - switch (pa_context_get_state(context)) { + switch (new_state) { case PA_CONTEXT_READY: - if (po->mixer != nullptr) - pulse_mixer_on_connect(*po->mixer, context); + if (mixer != nullptr) + pulse_mixer_on_connect(*mixer, context); - pa_threaded_mainloop_signal(po->mainloop, 0); + Signal(); break; case PA_CONTEXT_TERMINATED: case PA_CONTEXT_FAILED: - if (po->mixer != nullptr) - pulse_mixer_on_disconnect(*po->mixer); + if (mixer != nullptr) + pulse_mixer_on_disconnect(*mixer); /* the caller thread might be waiting for these states */ - pa_threaded_mainloop_signal(po->mainloop, 0); + Signal(); break; case PA_CONTEXT_UNCONNECTED: @@ -206,230 +325,203 @@ pulse_output_context_state_cb(struct pa_context *context, void *userdata) } static void -pulse_output_subscribe_cb(pa_context *context, - pa_subscription_event_type_t t, - uint32_t idx, void *userdata) +pulse_output_context_state_cb(struct pa_context *context, void *userdata) +{ + PulseOutput &po = *(PulseOutput *)userdata; + + po.OnContextStateChanged(pa_context_get_state(context)); +} + +inline void +PulseOutput::OnServerLayoutChanged(pa_subscription_event_type_t t, + uint32_t idx) { - PulseOutput *po = (PulseOutput *)userdata; pa_subscription_event_type_t facility = pa_subscription_event_type_t(t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK); pa_subscription_event_type_t type = pa_subscription_event_type_t(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK); - if (po->mixer != nullptr && + if (mixer != nullptr && facility == PA_SUBSCRIPTION_EVENT_SINK_INPUT && - po->stream != nullptr && - pa_stream_get_state(po->stream) == PA_STREAM_READY && - idx == pa_stream_get_index(po->stream) && + stream != nullptr && + pa_stream_get_state(stream) == PA_STREAM_READY && + idx == pa_stream_get_index(stream) && (type == PA_SUBSCRIPTION_EVENT_NEW || type == PA_SUBSCRIPTION_EVENT_CHANGE)) - pulse_mixer_on_change(*po->mixer, context, po->stream); + pulse_mixer_on_change(*mixer, context, stream); } -/** - * Attempt to connect asynchronously to the PulseAudio server. - * - * @return true on success, false on error - */ -static bool -pulse_output_connect(PulseOutput *po, Error &error) +static void +pulse_output_subscribe_cb(gcc_unused pa_context *context, + pa_subscription_event_type_t t, + uint32_t idx, void *userdata) { - assert(po != nullptr); - assert(po->context != nullptr); + PulseOutput &po = *(PulseOutput *)userdata; - if (pa_context_connect(po->context, po->server, + po.OnServerLayoutChanged(t, idx); +} + +inline bool +PulseOutput::Connect(Error &error) +{ + assert(context != nullptr); + + if (pa_context_connect(context, server, (pa_context_flags_t)0, nullptr) < 0) { - SetError(error, po->context, - "pa_context_connect() has failed"); + SetPulseError(error, context, + "pa_context_connect() has failed"); return false; } return true; } -/** - * Frees and clears the stream. - */ -static void -pulse_output_delete_stream(PulseOutput *po) +void +PulseOutput::DeleteStream() { - assert(po != nullptr); - assert(po->stream != nullptr); + assert(stream != nullptr); - pa_stream_set_suspended_callback(po->stream, nullptr, nullptr); + pa_stream_set_suspended_callback(stream, nullptr, nullptr); - pa_stream_set_state_callback(po->stream, nullptr, nullptr); - pa_stream_set_write_callback(po->stream, nullptr, nullptr); + pa_stream_set_state_callback(stream, nullptr, nullptr); + pa_stream_set_write_callback(stream, nullptr, nullptr); - pa_stream_disconnect(po->stream); - pa_stream_unref(po->stream); - po->stream = nullptr; + pa_stream_disconnect(stream); + pa_stream_unref(stream); + stream = nullptr; } -/** - * Frees and clears the context. - * - * Caller must lock the main loop. - */ -static void -pulse_output_delete_context(PulseOutput *po) +void +PulseOutput::DeleteContext() { - assert(po != nullptr); - assert(po->context != nullptr); + assert(context != nullptr); - pa_context_set_state_callback(po->context, nullptr, nullptr); - pa_context_set_subscribe_callback(po->context, nullptr, nullptr); + pa_context_set_state_callback(context, nullptr, nullptr); + pa_context_set_subscribe_callback(context, nullptr, nullptr); - pa_context_disconnect(po->context); - pa_context_unref(po->context); - po->context = nullptr; + pa_context_disconnect(context); + pa_context_unref(context); + context = nullptr; } -/** - * Create, set up and connect a context. - * - * Caller must lock the main loop. - * - * @return true on success, false on error - */ -static bool -pulse_output_setup_context(PulseOutput *po, Error &error) +bool +PulseOutput::SetupContext(Error &error) { - assert(po != nullptr); - assert(po->mainloop != nullptr); + assert(mainloop != nullptr); - po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop), - MPD_PULSE_NAME); - if (po->context == nullptr) { - error.Set(pulse_output_domain, "pa_context_new() has failed"); + context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), + MPD_PULSE_NAME); + if (context == nullptr) { + error.Set(pulse_domain, "pa_context_new() has failed"); return false; } - pa_context_set_state_callback(po->context, - pulse_output_context_state_cb, po); - pa_context_set_subscribe_callback(po->context, - pulse_output_subscribe_cb, po); + pa_context_set_state_callback(context, + pulse_output_context_state_cb, this); + pa_context_set_subscribe_callback(context, + pulse_output_subscribe_cb, this); - if (!pulse_output_connect(po, error)) { - pulse_output_delete_context(po); + if (!Connect(error)) { + DeleteContext(); return false; } return true; } -static AudioOutput * -pulse_output_init(const config_param ¶m, Error &error) +inline bool +PulseOutput::Configure(const ConfigBlock &block, Error &error) { - PulseOutput *po; + if (!base.Configure(block, error)) + return false; + + name = block.GetBlockValue("name", "mpd_pulse"); + server = block.GetBlockValue("server"); + sink = block.GetBlockValue("sink"); + return true; +} + +PulseOutput * +PulseOutput::Create(const ConfigBlock &block, Error &error) +{ setenv("PULSE_PROP_media.role", "music", true); setenv("PULSE_PROP_application.icon_name", "mpd", true); - po = new PulseOutput(); - if (!po->base.Configure(param, error)) { + auto *po = new PulseOutput(); + if (!po->Configure(block, error)) { delete po; return nullptr; } - po->name = param.GetBlockValue("name", "mpd_pulse"); - po->server = param.GetBlockValue("server"); - po->sink = param.GetBlockValue("sink"); - - po->mixer = nullptr; - po->mainloop = nullptr; - po->context = nullptr; - po->stream = nullptr; - - return &po->base; + return po; } -static void -pulse_output_finish(AudioOutput *ao) +inline bool +PulseOutput::Enable(Error &error) { - PulseOutput *po = (PulseOutput *)ao; - - delete po; -} - -static bool -pulse_output_enable(AudioOutput *ao, Error &error) -{ - PulseOutput *po = (PulseOutput *)ao; - - assert(po->mainloop == nullptr); - assert(po->context == nullptr); + assert(mainloop == nullptr); /* create the libpulse mainloop and start the thread */ - po->mainloop = pa_threaded_mainloop_new(); - if (po->mainloop == nullptr) { - error.Set(pulse_output_domain, + mainloop = pa_threaded_mainloop_new(); + if (mainloop == nullptr) { + error.Set(pulse_domain, "pa_threaded_mainloop_new() has failed"); return false; } - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); - if (pa_threaded_mainloop_start(po->mainloop) < 0) { - pa_threaded_mainloop_unlock(po->mainloop); - pa_threaded_mainloop_free(po->mainloop); - po->mainloop = nullptr; + if (pa_threaded_mainloop_start(mainloop) < 0) { + pa_threaded_mainloop_unlock(mainloop); + pa_threaded_mainloop_free(mainloop); + mainloop = nullptr; - error.Set(pulse_output_domain, + error.Set(pulse_domain, "pa_threaded_mainloop_start() has failed"); return false; } /* create the libpulse context and connect it */ - if (!pulse_output_setup_context(po, error)) { - pa_threaded_mainloop_unlock(po->mainloop); - pa_threaded_mainloop_stop(po->mainloop); - pa_threaded_mainloop_free(po->mainloop); - po->mainloop = nullptr; + if (!SetupContext(error)) { + pa_threaded_mainloop_unlock(mainloop); + pa_threaded_mainloop_stop(mainloop); + pa_threaded_mainloop_free(mainloop); + mainloop = nullptr; return false; } - pa_threaded_mainloop_unlock(po->mainloop); + pa_threaded_mainloop_unlock(mainloop); return true; } -static void -pulse_output_disable(AudioOutput *ao) +inline void +PulseOutput::Disable() { - PulseOutput *po = (PulseOutput *)ao; - - assert(po->mainloop != nullptr); + assert(mainloop != nullptr); - pa_threaded_mainloop_stop(po->mainloop); - if (po->context != nullptr) - pulse_output_delete_context(po); - pa_threaded_mainloop_free(po->mainloop); - po->mainloop = nullptr; + pa_threaded_mainloop_stop(mainloop); + if (context != nullptr) + DeleteContext(); + pa_threaded_mainloop_free(mainloop); + mainloop = nullptr; } -/** - * Check if the context is (already) connected, and waits if not. If - * the context has been disconnected, retry to connect. - * - * Caller must lock the main loop. - * - * @return true on success, false on error - */ -static bool -pulse_output_wait_connection(PulseOutput *po, Error &error) +bool +PulseOutput::WaitConnection(Error &error) { - assert(po->mainloop != nullptr); + assert(mainloop != nullptr); pa_context_state_t state; - if (po->context == nullptr && !pulse_output_setup_context(po, error)) + if (context == nullptr && !SetupContext(error)) return false; while (true) { - state = pa_context_get_state(po->context); + state = pa_context_get_state(context); switch (state) { case PA_CONTEXT_READY: /* nothing to do */ @@ -439,56 +531,61 @@ pulse_output_wait_connection(PulseOutput *po, Error &error) case PA_CONTEXT_TERMINATED: case PA_CONTEXT_FAILED: /* failure */ - SetError(error, po->context, "failed to connect"); - pulse_output_delete_context(po); + SetPulseError(error, context, "failed to connect"); + DeleteContext(); return false; case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: /* wait some more */ - pa_threaded_mainloop_wait(po->mainloop); + pa_threaded_mainloop_wait(mainloop); break; } } } -static void -pulse_output_stream_suspended_cb(gcc_unused pa_stream *stream, void *userdata) +inline void +PulseOutput::OnStreamSuspended(gcc_unused pa_stream *_stream) { - PulseOutput *po = (PulseOutput *)userdata; - - assert(stream == po->stream || po->stream == nullptr); - assert(po->mainloop != nullptr); + assert(_stream == stream || stream == nullptr); + assert(mainloop != nullptr); /* wake up the main loop to break out of the loop in pulse_output_play() */ - pa_threaded_mainloop_signal(po->mainloop, 0); + Signal(); } static void -pulse_output_stream_state_cb(pa_stream *stream, void *userdata) +pulse_output_stream_suspended_cb(pa_stream *stream, void *userdata) { - PulseOutput *po = (PulseOutput *)userdata; + PulseOutput &po = *(PulseOutput *)userdata; - assert(stream == po->stream || po->stream == nullptr); - assert(po->mainloop != nullptr); - assert(po->context != nullptr); + po.OnStreamSuspended(stream); +} + +inline void +PulseOutput::OnStreamStateChanged(pa_stream *_stream, + pa_stream_state_t new_state) +{ + assert(_stream == stream || stream == nullptr); + assert(mainloop != nullptr); + assert(context != nullptr); - switch (pa_stream_get_state(stream)) { + switch (new_state) { case PA_STREAM_READY: - if (po->mixer != nullptr) - pulse_mixer_on_change(*po->mixer, po->context, stream); + if (mixer != nullptr) + pulse_mixer_on_change(*mixer, context, _stream); - pa_threaded_mainloop_signal(po->mainloop, 0); + Signal(); break; case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: - if (po->mixer != nullptr) - pulse_mixer_on_disconnect(*po->mixer); + if (mixer != nullptr) + pulse_mixer_on_disconnect(*mixer); - pa_threaded_mainloop_signal(po->mainloop, 0); + Signal(); break; case PA_STREAM_UNCONNECTED: @@ -498,68 +595,75 @@ pulse_output_stream_state_cb(pa_stream *stream, void *userdata) } static void +pulse_output_stream_state_cb(pa_stream *stream, void *userdata) +{ + PulseOutput &po = *(PulseOutput *)userdata; + + return po.OnStreamStateChanged(stream, pa_stream_get_state(stream)); +} + +inline void +PulseOutput::OnStreamWrite(size_t nbytes) +{ + assert(mainloop != nullptr); + + writable = nbytes; + Signal(); +} + +static void pulse_output_stream_write_cb(gcc_unused pa_stream *stream, size_t nbytes, void *userdata) { - PulseOutput *po = (PulseOutput *)userdata; - - assert(po->mainloop != nullptr); + PulseOutput &po = *(PulseOutput *)userdata; - po->writable = nbytes; - pa_threaded_mainloop_signal(po->mainloop, 0); + return po.OnStreamWrite(nbytes); } -/** - * Create, set up and connect a context. - * - * Caller must lock the main loop. - * - * @return true on success, false on error - */ -static bool -pulse_output_setup_stream(PulseOutput *po, const pa_sample_spec *ss, - Error &error) +inline bool +PulseOutput::SetupStream(const pa_sample_spec &ss, Error &error) { - assert(po != nullptr); - assert(po->context != nullptr); + assert(context != nullptr); - po->stream = pa_stream_new(po->context, po->name, ss, nullptr); - if (po->stream == nullptr) { - SetError(error, po->context, "pa_stream_new() has failed"); + /* WAVE-EX is been adopted as the speaker map for most media files */ + pa_channel_map chan_map; + pa_channel_map_init_auto(&chan_map, ss.channels, + PA_CHANNEL_MAP_WAVEEX); + stream = pa_stream_new(context, name, &ss, &chan_map); + if (stream == nullptr) { + SetPulseError(error, context, + "pa_stream_new() has failed"); return false; } - pa_stream_set_suspended_callback(po->stream, - pulse_output_stream_suspended_cb, po); + pa_stream_set_suspended_callback(stream, + pulse_output_stream_suspended_cb, + this); - pa_stream_set_state_callback(po->stream, - pulse_output_stream_state_cb, po); - pa_stream_set_write_callback(po->stream, - pulse_output_stream_write_cb, po); + pa_stream_set_state_callback(stream, + pulse_output_stream_state_cb, this); + pa_stream_set_write_callback(stream, + pulse_output_stream_write_cb, this); return true; } -static bool -pulse_output_open(AudioOutput *ao, AudioFormat &audio_format, - Error &error) +inline bool +PulseOutput::Open(AudioFormat &audio_format, Error &error) { - PulseOutput *po = (PulseOutput *)ao; - pa_sample_spec ss; - - assert(po->mainloop != nullptr); + assert(mainloop != nullptr); - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); - if (po->context != nullptr) { - switch (pa_context_get_state(po->context)) { + if (context != nullptr) { + switch (pa_context_get_state(context)) { case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_TERMINATED: case PA_CONTEXT_FAILED: /* the connection was closed meanwhile; delete it, and pulse_output_wait_connection() will reopen it */ - pulse_output_delete_context(po); + DeleteContext(); break; case PA_CONTEXT_READY: @@ -570,8 +674,8 @@ pulse_output_open(AudioOutput *ao, AudioFormat &audio_format, } } - if (!pulse_output_wait_connection(po, error)) { - pa_threaded_mainloop_unlock(po->mainloop); + if (!WaitConnection(error)) { + pa_threaded_mainloop_unlock(mainloop); return false; } @@ -579,304 +683,282 @@ pulse_output_open(AudioOutput *ao, AudioFormat &audio_format, we just force MPD to send us everything as 16 bit */ audio_format.format = SampleFormat::S16; + pa_sample_spec ss; ss.format = PA_SAMPLE_S16NE; ss.rate = audio_format.sample_rate; ss.channels = audio_format.channels; /* create a stream .. */ - if (!pulse_output_setup_stream(po, &ss, error)) { - pa_threaded_mainloop_unlock(po->mainloop); + if (!SetupStream(ss, error)) { + pa_threaded_mainloop_unlock(mainloop); return false; } /* .. and connect it (asynchronously) */ - if (pa_stream_connect_playback(po->stream, po->sink, + if (pa_stream_connect_playback(stream, sink, nullptr, pa_stream_flags_t(0), nullptr, nullptr) < 0) { - pulse_output_delete_stream(po); + DeleteStream(); - SetError(error, po->context, - "pa_stream_connect_playback() has failed"); - pa_threaded_mainloop_unlock(po->mainloop); + SetPulseError(error, context, + "pa_stream_connect_playback() has failed"); + pa_threaded_mainloop_unlock(mainloop); return false; } - pa_threaded_mainloop_unlock(po->mainloop); - + pa_threaded_mainloop_unlock(mainloop); return true; } -static void -pulse_output_close(AudioOutput *ao) +inline void +PulseOutput::Close() { - PulseOutput *po = (PulseOutput *)ao; - pa_operation *o; - - assert(po->mainloop != nullptr); + assert(mainloop != nullptr); - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); - if (pa_stream_get_state(po->stream) == PA_STREAM_READY) { - o = pa_stream_drain(po->stream, - pulse_output_stream_success_cb, po); + if (pa_stream_get_state(stream) == PA_STREAM_READY) { + pa_operation *o = + pa_stream_drain(stream, + pulse_output_stream_success_cb, this); if (o == nullptr) { - FormatWarning(pulse_output_domain, - "pa_stream_drain() has failed: %s", - pa_strerror(pa_context_errno(po->context))); + LogPulseError(context, + "pa_stream_drain() has failed"); } else - pulse_wait_for_operation(po->mainloop, o); + pulse_wait_for_operation(mainloop, o); } - pulse_output_delete_stream(po); + DeleteStream(); - if (po->context != nullptr && - pa_context_get_state(po->context) != PA_CONTEXT_READY) - pulse_output_delete_context(po); + if (context != nullptr && + pa_context_get_state(context) != PA_CONTEXT_READY) + DeleteContext(); - pa_threaded_mainloop_unlock(po->mainloop); + pa_threaded_mainloop_unlock(mainloop); } -/** - * Check if the stream is (already) connected, and waits if not. The - * mainloop must be locked before calling this function. - * - * @return true on success, false on error - */ -static bool -pulse_output_wait_stream(PulseOutput *po, Error &error) +bool +PulseOutput::WaitStream(Error &error) { while (true) { - switch (pa_stream_get_state(po->stream)) { + switch (pa_stream_get_state(stream)) { case PA_STREAM_READY: return true; case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: case PA_STREAM_UNCONNECTED: - SetError(error, po->context, - "failed to connect the stream"); + SetPulseError(error, context, + "failed to connect the stream"); return false; case PA_STREAM_CREATING: - pa_threaded_mainloop_wait(po->mainloop); + pa_threaded_mainloop_wait(mainloop); break; } } } -/** - * Sets cork mode on the stream. - */ -static bool -pulse_output_stream_pause(PulseOutput *po, bool pause, - Error &error) +bool +PulseOutput::StreamPause(bool pause, Error &error) { - pa_operation *o; - - assert(po->mainloop != nullptr); - assert(po->context != nullptr); - assert(po->stream != nullptr); + assert(mainloop != nullptr); + assert(context != nullptr); + assert(stream != nullptr); - o = pa_stream_cork(po->stream, pause, - pulse_output_stream_success_cb, po); + pa_operation *o = pa_stream_cork(stream, pause, + pulse_output_stream_success_cb, this); if (o == nullptr) { - SetError(error, po->context, "pa_stream_cork() has failed"); + SetPulseError(error, context, + "pa_stream_cork() has failed"); return false; } - if (!pulse_wait_for_operation(po->mainloop, o)) { - SetError(error, po->context, "pa_stream_cork() has failed"); + if (!pulse_wait_for_operation(mainloop, o)) { + SetPulseError(error, context, + "pa_stream_cork() has failed"); return false; } return true; } -static unsigned -pulse_output_delay(AudioOutput *ao) +inline unsigned +PulseOutput::Delay() { - PulseOutput *po = (PulseOutput *)ao; - unsigned result = 0; - - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); - if (po->base.pause && pa_stream_is_corked(po->stream) && - pa_stream_get_state(po->stream) == PA_STREAM_READY) + unsigned result = 0; + if (base.pause && pa_stream_is_corked(stream) && + pa_stream_get_state(stream) == PA_STREAM_READY) /* idle while paused */ result = 1000; - pa_threaded_mainloop_unlock(po->mainloop); + pa_threaded_mainloop_unlock(mainloop); return result; } -static size_t -pulse_output_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +PulseOutput::Play(const void *chunk, size_t size, Error &error) { - PulseOutput *po = (PulseOutput *)ao; - - assert(po->mainloop != nullptr); - assert(po->stream != nullptr); + assert(mainloop != nullptr); + assert(stream != nullptr); - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); /* check if the stream is (already) connected */ - if (!pulse_output_wait_stream(po, error)) { - pa_threaded_mainloop_unlock(po->mainloop); + if (!WaitStream(error)) { + pa_threaded_mainloop_unlock(mainloop); return 0; } - assert(po->context != nullptr); + assert(context != nullptr); /* unpause if previously paused */ - if (pa_stream_is_corked(po->stream) && - !pulse_output_stream_pause(po, false, error)) { - pa_threaded_mainloop_unlock(po->mainloop); + if (pa_stream_is_corked(stream) && !StreamPause(false, error)) { + pa_threaded_mainloop_unlock(mainloop); return 0; } /* wait until the server allows us to write */ - while (po->writable == 0) { - if (pa_stream_is_suspended(po->stream)) { - pa_threaded_mainloop_unlock(po->mainloop); - error.Set(pulse_output_domain, "suspended"); + while (writable == 0) { + if (pa_stream_is_suspended(stream)) { + pa_threaded_mainloop_unlock(mainloop); + error.Set(pulse_domain, "suspended"); return 0; } - pa_threaded_mainloop_wait(po->mainloop); + pa_threaded_mainloop_wait(mainloop); - if (pa_stream_get_state(po->stream) != PA_STREAM_READY) { - pa_threaded_mainloop_unlock(po->mainloop); - error.Set(pulse_output_domain, "disconnected"); + if (pa_stream_get_state(stream) != PA_STREAM_READY) { + pa_threaded_mainloop_unlock(mainloop); + error.Set(pulse_domain, "disconnected"); return 0; } } /* now write */ - if (size > po->writable) + if (size > writable) /* don't send more than possible */ - size = po->writable; + size = writable; - po->writable -= size; + writable -= size; - int result = pa_stream_write(po->stream, chunk, size, nullptr, + int result = pa_stream_write(stream, chunk, size, nullptr, 0, PA_SEEK_RELATIVE); - pa_threaded_mainloop_unlock(po->mainloop); + pa_threaded_mainloop_unlock(mainloop); if (result < 0) { - SetError(error, po->context, "pa_stream_write() failed"); + SetPulseError(error, context, "pa_stream_write() failed"); return 0; } return size; } -static void -pulse_output_cancel(AudioOutput *ao) +inline void +PulseOutput::Cancel() { - PulseOutput *po = (PulseOutput *)ao; - pa_operation *o; - - assert(po->mainloop != nullptr); - assert(po->stream != nullptr); + assert(mainloop != nullptr); + assert(stream != nullptr); - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); - if (pa_stream_get_state(po->stream) != PA_STREAM_READY) { + if (pa_stream_get_state(stream) != PA_STREAM_READY) { /* no need to flush when the stream isn't connected yet */ - pa_threaded_mainloop_unlock(po->mainloop); + pa_threaded_mainloop_unlock(mainloop); return; } - assert(po->context != nullptr); + assert(context != nullptr); - o = pa_stream_flush(po->stream, pulse_output_stream_success_cb, po); + pa_operation *o = pa_stream_flush(stream, + pulse_output_stream_success_cb, + this); if (o == nullptr) { - FormatWarning(pulse_output_domain, - "pa_stream_flush() has failed: %s", - pa_strerror(pa_context_errno(po->context))); - pa_threaded_mainloop_unlock(po->mainloop); + LogPulseError(context, "pa_stream_flush() has failed"); + pa_threaded_mainloop_unlock(mainloop); return; } - pulse_wait_for_operation(po->mainloop, o); - pa_threaded_mainloop_unlock(po->mainloop); + pulse_wait_for_operation(mainloop, o); + pa_threaded_mainloop_unlock(mainloop); } -static bool -pulse_output_pause(AudioOutput *ao) +inline bool +PulseOutput::Pause() { - PulseOutput *po = (PulseOutput *)ao; - - assert(po->mainloop != nullptr); - assert(po->stream != nullptr); + assert(mainloop != nullptr); + assert(stream != nullptr); - pa_threaded_mainloop_lock(po->mainloop); + pa_threaded_mainloop_lock(mainloop); /* check if the stream is (already/still) connected */ Error error; - if (!pulse_output_wait_stream(po, error)) { - pa_threaded_mainloop_unlock(po->mainloop); + if (!WaitStream(error)) { + pa_threaded_mainloop_unlock(mainloop); LogError(error); return false; } - assert(po->context != nullptr); + assert(context != nullptr); /* cork the stream */ - if (!pa_stream_is_corked(po->stream) && - !pulse_output_stream_pause(po, true, error)) { - pa_threaded_mainloop_unlock(po->mainloop); + if (!pa_stream_is_corked(stream) && !StreamPause(true, error)) { + pa_threaded_mainloop_unlock(mainloop); LogError(error); return false; } - pa_threaded_mainloop_unlock(po->mainloop); - + pa_threaded_mainloop_unlock(mainloop); return true; } -static bool -pulse_output_test_default_device(void) +inline bool +PulseOutput::TestDefaultDevice() { - bool success; - - const config_param empty; - PulseOutput *po = (PulseOutput *) - pulse_output_init(empty, IgnoreError()); + const ConfigBlock empty; + PulseOutput *po = PulseOutput::Create(empty, IgnoreError()); if (po == nullptr) return false; - success = pulse_output_wait_connection(po, IgnoreError()); - pulse_output_finish(&po->base); - + bool success = po->WaitConnection(IgnoreError()); + delete po; return success; } +static bool +pulse_output_test_default_device(void) +{ + return PulseOutput::TestDefaultDevice(); +} + +typedef AudioOutputWrapper<PulseOutput> Wrapper; + const struct AudioOutputPlugin pulse_output_plugin = { "pulse", pulse_output_test_default_device, - pulse_output_init, - pulse_output_finish, - pulse_output_enable, - pulse_output_disable, - pulse_output_open, - pulse_output_close, - pulse_output_delay, + &Wrapper::Init, + &Wrapper::Finish, + &Wrapper::Enable, + &Wrapper::Disable, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, nullptr, - pulse_output_play, + &Wrapper::Play, nullptr, - pulse_output_cancel, - pulse_output_pause, + &Wrapper::Cancel, + &Wrapper::Pause, &pulse_mixer_plugin, }; diff --git a/src/output/plugins/PulseOutputPlugin.hxx b/src/output/plugins/PulseOutputPlugin.hxx index 9219780a5..f193db1d8 100644 --- a/src/output/plugins/PulseOutputPlugin.hxx +++ b/src/output/plugins/PulseOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #ifndef MPD_PULSE_OUTPUT_PLUGIN_HXX #define MPD_PULSE_OUTPUT_PLUGIN_HXX -struct PulseOutput; +class PulseOutput; class PulseMixer; struct pa_cvolume; class Error; diff --git a/src/output/plugins/RecorderOutputPlugin.cxx b/src/output/plugins/RecorderOutputPlugin.cxx index 87e23f55a..4f9231c70 100644 --- a/src/output/plugins/RecorderOutputPlugin.cxx +++ b/src/output/plugins/RecorderOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,21 +20,28 @@ #include "config.h" #include "RecorderOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" +#include "tag/Format.hxx" +#include "encoder/ToOutputStream.hxx" +#include "encoder/EncoderInterface.hxx" #include "encoder/EncoderPlugin.hxx" #include "encoder/EncoderList.hxx" #include "config/ConfigError.hxx" +#include "config/ConfigPath.hxx" +#include "Log.hxx" +#include "fs/AllocatedPath.hxx" +#include "fs/io/FileOutputStream.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#include "system/fd_util.h" -#include "open.h" #include <assert.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <errno.h> +#include <stdlib.h> + +static constexpr Domain recorder_domain("recorder"); + +class RecorderOutput { + friend struct AudioOutputWrapper<RecorderOutput>; -struct RecorderOutput { AudioOutput base; /** @@ -45,44 +52,77 @@ struct RecorderOutput { /** * The destination file name. */ - const char *path; + AllocatedPath path; + + /** + * A string that will be used with FormatTag() to build the + * destination path. + */ + std::string format_path; /** - * The destination file descriptor. + * The #AudioFormat that is currently active. This is used + * for switching to another file. */ - int fd; + AudioFormat effective_audio_format; /** - * The buffer for encoder_read(). + * The destination file. */ - char buffer[32768]; + FileOutputStream *file; RecorderOutput() - :base(recorder_output_plugin) {} + :base(recorder_output_plugin), + encoder(nullptr), + path(AllocatedPath::Null()) {} - bool Initialize(const config_param ¶m, Error &error_r) { - return base.Configure(param, error_r); + ~RecorderOutput() { + if (encoder != nullptr) + encoder->Dispose(); } - bool Configure(const config_param ¶m, Error &error); + bool Initialize(const ConfigBlock &block, Error &error_r) { + return base.Configure(block, error_r); + } + + static RecorderOutput *Create(const ConfigBlock &block, Error &error); + + bool Configure(const ConfigBlock &block, Error &error); - bool WriteToFile(const void *data, size_t length, Error &error); + bool Open(AudioFormat &audio_format, Error &error); + void Close(); /** * Writes pending data from the encoder to the output file. */ bool EncoderToFile(Error &error); -}; -static constexpr Domain recorder_output_domain("recorder_output"); + void SendTag(const Tag &tag); + + size_t Play(const void *chunk, size_t size, Error &error); + +private: + gcc_pure + bool HasDynamicPath() const { + return !format_path.empty(); + } + + /** + * Finish the encoder and commit the file. + */ + bool Commit(Error &error); + + void FinishFormat(); + bool ReopenFormat(AllocatedPath &&new_path, Error &error); +}; inline bool -RecorderOutput::Configure(const config_param ¶m, Error &error) +RecorderOutput::Configure(const ConfigBlock &block, Error &error) { /* read configuration */ const char *encoder_name = - param.GetBlockValue("encoder", "vorbis"); + block.GetBlockValue("encoder", "vorbis"); const auto encoder_plugin = encoder_plugin_get(encoder_name); if (encoder_plugin == nullptr) { error.Format(config_domain, @@ -90,167 +130,269 @@ RecorderOutput::Configure(const config_param ¶m, Error &error) return false; } - path = param.GetBlockValue("path"); - if (path == nullptr) { + path = block.GetBlockPath("path", error); + if (error.IsDefined()) + return false; + + const char *fmt = block.GetBlockValue("format_path", nullptr); + if (fmt != nullptr) + format_path = fmt; + + if (path.IsNull() && fmt == nullptr) { error.Set(config_domain, "'path' not configured"); return false; } + if (!path.IsNull() && fmt != nullptr) { + error.Set(config_domain, "Cannot have both 'path' and 'format_path'"); + return false; + } + /* initialize encoder */ - encoder = encoder_init(*encoder_plugin, param, error); + encoder = encoder_init(*encoder_plugin, block, error); if (encoder == nullptr) return false; return true; } -static AudioOutput * -recorder_output_init(const config_param ¶m, Error &error) +RecorderOutput * +RecorderOutput::Create(const ConfigBlock &block, Error &error) { RecorderOutput *recorder = new RecorderOutput(); - if (!recorder->Initialize(param, error)) { + if (!recorder->Initialize(block, error)) { delete recorder; return nullptr; } - if (!recorder->Configure(param, error)) { + if (!recorder->Configure(block, error)) { delete recorder; return nullptr; } - return &recorder->base; + return recorder; } -static void -recorder_output_finish(AudioOutput *ao) +inline bool +RecorderOutput::EncoderToFile(Error &error) { - RecorderOutput *recorder = (RecorderOutput *)ao; + assert(file != nullptr); + assert(file->IsDefined()); - encoder_finish(recorder->encoder); - delete recorder; + return EncoderToOutputStream(*file, *encoder, error); } inline bool -RecorderOutput::WriteToFile(const void *_data, size_t length, Error &error) +RecorderOutput::Open(AudioFormat &audio_format, Error &error) { - assert(length > 0); - - const uint8_t *data = (const uint8_t *)_data, *end = data + length; - - while (true) { - ssize_t nbytes = write(fd, data, end - data); - if (nbytes > 0) { - data += nbytes; - if (data == end) - return true; - } else if (nbytes == 0) { - /* shouldn't happen for files */ - error.Set(recorder_output_domain, - "write() returned 0"); + /* create the output file */ + + if (!HasDynamicPath()) { + assert(!path.IsNull()); + + file = FileOutputStream::Create(path, error); + if (file == nullptr) return false; - } else if (errno != EINTR) { - error.FormatErrno("Failed to write to '%s'", path); + } else { + /* don't open the file just yet; wait until we have + a tag that we can use to build the path */ + assert(path.IsNull()); + + file = nullptr; + } + + /* open the encoder */ + + if (!encoder->Open(audio_format, error)) { + delete file; + return false; + } + + if (!HasDynamicPath()) { + if (!EncoderToFile(error)) { + encoder->Close(); + delete file; return false; } + } else { + /* remember the AudioFormat for ReopenFormat() */ + effective_audio_format = audio_format; + + /* close the encoder for now; it will be opened as + soon as we have received a tag */ + encoder->Close(); } + + return true; } inline bool -RecorderOutput::EncoderToFile(Error &error) +RecorderOutput::Commit(Error &error) { - assert(fd >= 0); + assert(!path.IsNull()); - while (true) { - /* read from the encoder */ + /* flush the encoder and write the rest to the file */ - size_t size = encoder_read(encoder, buffer, sizeof(buffer)); - if (size == 0) - return true; + bool success = encoder_end(encoder, error) && + EncoderToFile(error); - /* write everything into the file */ + /* now really close everything */ - if (!WriteToFile(buffer, size, error)) - return false; - } + encoder->Close(); + + if (success && !file->Commit(error)) + success = false; + + delete file; + + return success; } -static bool -recorder_output_open(AudioOutput *ao, - AudioFormat &audio_format, - Error &error) +inline void +RecorderOutput::Close() { - RecorderOutput *recorder = (RecorderOutput *)ao; + if (file == nullptr) { + /* not currently encoding to a file; nothing needs to + be done now */ + assert(HasDynamicPath()); + assert(path.IsNull()); + return; + } - /* create the output file */ + Error error; + if (!Commit(error)) + LogError(error); - recorder->fd = open_cloexec(recorder->path, - O_CREAT|O_WRONLY|O_TRUNC|O_BINARY, - 0666); - if (recorder->fd < 0) { - error.FormatErrno("Failed to create '%s'", recorder->path); - return false; + if (HasDynamicPath()) { + assert(!path.IsNull()); + path.SetNull(); } +} - /* open the encoder */ +void +RecorderOutput::FinishFormat() +{ + assert(HasDynamicPath()); + + if (file == nullptr) + return; + + Error error; + if (!Commit(error)) + LogError(error); + + file = nullptr; + path.SetNull(); +} - if (!encoder_open(recorder->encoder, audio_format, error)) { - close(recorder->fd); - unlink(recorder->path); +inline bool +RecorderOutput::ReopenFormat(AllocatedPath &&new_path, Error &error) +{ + assert(HasDynamicPath()); + assert(path.IsNull()); + assert(file == nullptr); + + FileOutputStream *new_file = + FileOutputStream::Create(new_path, error); + if (new_file == nullptr) + return false; + + AudioFormat new_audio_format = effective_audio_format; + if (!encoder->Open(new_audio_format, error)) { + delete new_file; return false; } - if (!recorder->EncoderToFile(error)) { - encoder_close(recorder->encoder); - close(recorder->fd); - unlink(recorder->path); + /* reopening the encoder must always result in the same + AudioFormat as before */ + assert(new_audio_format == effective_audio_format); + + if (!EncoderToOutputStream(*new_file, *encoder, error)) { + encoder->Close(); + delete new_file; return false; } + path = std::move(new_path); + file = new_file; + + FormatDebug(recorder_domain, "Recording to \"%s\"", + path.ToUTF8().c_str()); + return true; } -static void -recorder_output_close(AudioOutput *ao) +inline void +RecorderOutput::SendTag(const Tag &tag) { - RecorderOutput *recorder = (RecorderOutput *)ao; - - /* flush the encoder and write the rest to the file */ + if (HasDynamicPath()) { + char *p = FormatTag(tag, format_path.c_str()); + if (p == nullptr || *p == 0) { + /* no path could be composed with this tag: + don't write a file */ + free(p); + FinishFormat(); + return; + } - if (encoder_end(recorder->encoder, IgnoreError())) - recorder->EncoderToFile(IgnoreError()); + Error error; + AllocatedPath new_path = ParsePath(p, error); + free(p); + if (new_path.IsNull()) { + LogError(error); + FinishFormat(); + return; + } - /* now really close everything */ + if (new_path != path) { + FinishFormat(); - encoder_close(recorder->encoder); + if (!ReopenFormat(std::move(new_path), error)) { + LogError(error); + return; + } + } + } - close(recorder->fd); + Error error; + if (!encoder_pre_tag(encoder, error) || + !EncoderToFile(error) || + !encoder_tag(encoder, tag, error)) + LogError(error); } -static size_t -recorder_output_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) +inline size_t +RecorderOutput::Play(const void *chunk, size_t size, Error &error) { - RecorderOutput *recorder = (RecorderOutput *)ao; + if (file == nullptr) { + /* not currently encoding to a file; discard incoming + data */ + assert(HasDynamicPath()); + assert(path.IsNull()); + return size; + } - return encoder_write(recorder->encoder, chunk, size, error) && - recorder->EncoderToFile(error) + return encoder_write(encoder, chunk, size, error) && + EncoderToFile(error) ? size : 0; } +typedef AudioOutputWrapper<RecorderOutput> Wrapper; + const struct AudioOutputPlugin recorder_output_plugin = { "recorder", nullptr, - recorder_output_init, - recorder_output_finish, - nullptr, + &Wrapper::Init, + &Wrapper::Finish, nullptr, - recorder_output_open, - recorder_output_close, nullptr, + &Wrapper::Open, + &Wrapper::Close, nullptr, - recorder_output_play, + &Wrapper::SendTag, + &Wrapper::Play, nullptr, nullptr, nullptr, diff --git a/src/output/plugins/RecorderOutputPlugin.hxx b/src/output/plugins/RecorderOutputPlugin.hxx index ea1044e0f..061279fba 100644 --- a/src/output/plugins/RecorderOutputPlugin.hxx +++ b/src/output/plugins/RecorderOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/RoarOutputPlugin.cxx b/src/output/plugins/RoarOutputPlugin.cxx index aa37c91b7..11b0f1671 100644 --- a/src/output/plugins/RoarOutputPlugin.cxx +++ b/src/output/plugins/RoarOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen * @@ -21,6 +21,7 @@ #include "config.h" #include "RoarOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "../Wrapper.hxx" #include "mixer/MixerList.hxx" #include "thread/Mutex.hxx" #include "util/Error.hxx" @@ -36,6 +37,8 @@ #undef new class RoarOutput { + friend struct AudioOutputWrapper<RoarOutput>; + AudioOutput base; std::string host, name; @@ -57,11 +60,11 @@ public: return &base; } - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); } - void Configure(const config_param ¶m); + void Configure(const ConfigBlock &block); bool Open(AudioFormat &audio_format, Error &error); void Close(); @@ -121,40 +124,32 @@ roar_output_set_volume(RoarOutput &roar, unsigned volume) } inline void -RoarOutput::Configure(const config_param ¶m) +RoarOutput::Configure(const ConfigBlock &block) { - host = param.GetBlockValue("server", ""); - name = param.GetBlockValue("name", "MPD"); + host = block.GetBlockValue("server", ""); + name = block.GetBlockValue("name", "MPD"); - const char *_role = param.GetBlockValue("role", "music"); + const char *_role = block.GetBlockValue("role", "music"); role = _role != nullptr ? roar_str2role(_role) : ROAR_ROLE_MUSIC; } static AudioOutput * -roar_init(const config_param ¶m, Error &error) +roar_init(const ConfigBlock &block, Error &error) { RoarOutput *self = new RoarOutput(); - if (!self->Initialize(param, error)) { + if (!self->Initialize(block, error)) { delete self; return nullptr; } - self->Configure(param); + self->Configure(block); return *self; } static void -roar_finish(AudioOutput *ao) -{ - RoarOutput *self = (RoarOutput *)ao; - - delete self; -} - -static void roar_use_audio_format(struct roar_audio_info *info, AudioFormat &audio_format) { @@ -221,14 +216,6 @@ RoarOutput::Open(AudioFormat &audio_format, Error &error) return true; } -static bool -roar_open(AudioOutput *ao, AudioFormat &audio_format, Error &error) -{ - RoarOutput *self = (RoarOutput *)ao; - - return self->Open(audio_format, error); -} - inline void RoarOutput::Close() { @@ -242,13 +229,6 @@ RoarOutput::Close() roar_disconnect(&con); } -static void -roar_close(AudioOutput *ao) -{ - RoarOutput *self = (RoarOutput *)ao; - self->Close(); -} - inline void RoarOutput::Cancel() { @@ -277,14 +257,6 @@ RoarOutput::Cancel() alive = true; } -static void -roar_cancel(AudioOutput *ao) -{ - RoarOutput *self = (RoarOutput *)ao; - - self->Cancel(); -} - inline size_t RoarOutput::Play(const void *chunk, size_t size, Error &error) { @@ -302,14 +274,6 @@ RoarOutput::Play(const void *chunk, size_t size, Error &error) return nbytes; } -static size_t -roar_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) -{ - RoarOutput *self = (RoarOutput *)ao; - return self->Play(chunk, size, error); -} - static const char* roar_tag_convert(TagType type, bool *is_uuid) { @@ -407,26 +371,28 @@ RoarOutput::SendTag(const Tag &tag) } static void -roar_send_tag(AudioOutput *ao, const Tag *meta) +roar_send_tag(AudioOutput *ao, const Tag &meta) { RoarOutput *self = (RoarOutput *)ao; - self->SendTag(*meta); + self->SendTag(meta); } +typedef AudioOutputWrapper<RoarOutput> Wrapper; + const struct AudioOutputPlugin roar_output_plugin = { "roar", nullptr, roar_init, - roar_finish, + &Wrapper::Finish, nullptr, nullptr, - roar_open, - roar_close, + &Wrapper::Open, + &Wrapper::Close, nullptr, roar_send_tag, - roar_play, + &Wrapper::Play, nullptr, - roar_cancel, + &Wrapper::Cancel, nullptr, &roar_mixer_plugin, }; diff --git a/src/output/plugins/RoarOutputPlugin.hxx b/src/output/plugins/RoarOutputPlugin.hxx index 5f5a9246e..a9726d5c8 100644 --- a/src/output/plugins/RoarOutputPlugin.hxx +++ b/src/output/plugins/RoarOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/ShoutOutputPlugin.cxx b/src/output/plugins/ShoutOutputPlugin.cxx index b51f7ed82..339c4e491 100644 --- a/src/output/plugins/ShoutOutputPlugin.cxx +++ b/src/output/plugins/ShoutOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "ShoutOutputPlugin.hxx" #include "../OutputAPI.hxx" +#include "encoder/EncoderInterface.hxx" #include "encoder/EncoderPlugin.hxx" #include "encoder/EncoderList.hxx" #include "config/ConfigError.hxx" @@ -67,11 +68,11 @@ struct ShoutOutput final { shout_free(shout_conn); } - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); } - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); }; static int shout_init_count; @@ -91,18 +92,18 @@ shout_encoder_plugin_get(const char *name) gcc_pure static const char * -require_block_string(const config_param ¶m, const char *name) +require_block_string(const ConfigBlock &block, const char *name) { - const char *value = param.GetBlockValue(name); + const char *value = block.GetBlockValue(name); if (value == nullptr) FormatFatalError("no \"%s\" defined for shout device defined " - "at line %u\n", name, param.line); + "at line %d\n", name, block.line); return value; } inline bool -ShoutOutput::Configure(const config_param ¶m, Error &error) +ShoutOutput::Configure(const ConfigBlock &block, Error &error) { const AudioFormat audio_format = base.config_audio_format; @@ -112,22 +113,22 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) return false; } - const char *host = require_block_string(param, "host"); - const char *mount = require_block_string(param, "mount"); - unsigned port = param.GetBlockValue("port", 0u); + const char *host = require_block_string(block, "host"); + const char *mount = require_block_string(block, "mount"); + unsigned port = block.GetBlockValue("port", 0u); if (port == 0) { error.Set(config_domain, "shout port must be configured"); return false; } - const char *passwd = require_block_string(param, "password"); - const char *name = require_block_string(param, "name"); + const char *passwd = require_block_string(block, "password"); + const char *name = require_block_string(block, "name"); - bool is_public = param.GetBlockValue("public", false); + bool is_public = block.GetBlockValue("public", false); - const char *user = param.GetBlockValue("user", "source"); + const char *user = block.GetBlockValue("user", "source"); - const char *value = param.GetBlockValue("quality"); + const char *value = block.GetBlockValue("quality"); if (value != nullptr) { char *test; quality = strtod(value, &test); @@ -140,14 +141,14 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) return false; } - if (param.GetBlockValue("bitrate") != nullptr) { + if (block.GetBlockValue("bitrate") != nullptr) { error.Set(config_domain, "quality and bitrate are " "both defined"); return false; } } else { - value = param.GetBlockValue("bitrate"); + value = block.GetBlockValue("bitrate"); if (value == nullptr) { error.Set(config_domain, "neither bitrate nor quality defined"); @@ -164,7 +165,7 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) } } - const char *encoding = param.GetBlockValue("encoding", "ogg"); + const char *encoding = block.GetBlockValue("encoding", "ogg"); const auto encoder_plugin = shout_encoder_plugin_get(encoding); if (encoder_plugin == nullptr) { error.Format(config_domain, @@ -173,7 +174,7 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) return false; } - encoder = encoder_init(*encoder_plugin, param, error); + encoder = encoder_init(*encoder_plugin, block, error); if (encoder == nullptr) return false; @@ -184,7 +185,7 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) shout_format = SHOUT_FORMAT_OGG; unsigned protocol; - value = param.GetBlockValue("protocol"); + value = block.GetBlockValue("protocol"); if (value != nullptr) { if (0 == strcmp(value, "shoutcast") && 0 != strcmp(encoding, "mp3")) { @@ -225,21 +226,21 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) } /* optional paramters */ - timeout = param.GetBlockValue("timeout", DEFAULT_CONN_TIMEOUT); + timeout = block.GetBlockValue("timeout", DEFAULT_CONN_TIMEOUT); - value = param.GetBlockValue("genre"); + value = block.GetBlockValue("genre"); if (value != nullptr && shout_set_genre(shout_conn, value)) { error.Set(shout_output_domain, shout_get_error(shout_conn)); return false; } - value = param.GetBlockValue("description"); + value = block.GetBlockValue("description"); if (value != nullptr && shout_set_description(shout_conn, value)) { error.Set(shout_output_domain, shout_get_error(shout_conn)); return false; } - value = param.GetBlockValue("url"); + value = block.GetBlockValue("url"); if (value != nullptr && shout_set_url(shout_conn, value)) { error.Set(shout_output_domain, shout_get_error(shout_conn)); return false; @@ -271,15 +272,15 @@ ShoutOutput::Configure(const config_param ¶m, Error &error) } static AudioOutput * -my_shout_init_driver(const config_param ¶m, Error &error) +my_shout_init_driver(const ConfigBlock &block, Error &error) { ShoutOutput *sd = new ShoutOutput(); - if (!sd->Initialize(param, error)) { + if (!sd->Initialize(block, error)) { delete sd; return nullptr; } - if (!sd->Configure(param, error)) { + if (!sd->Configure(block, error)) { delete sd; return nullptr; } @@ -345,7 +346,7 @@ static void close_shout_conn(ShoutOutput * sd) if (encoder_end(sd->encoder, IgnoreError())) write_page(sd, IgnoreError()); - encoder_close(sd->encoder); + sd->encoder->Close(); } if (shout_get_connected(sd->shout_conn) != SHOUTERR_UNCONNECTED && @@ -361,7 +362,7 @@ my_shout_finish_driver(AudioOutput *ao) { ShoutOutput *sd = (ShoutOutput *)ao; - encoder_finish(sd->encoder); + sd->encoder->Dispose(); delete sd; @@ -415,13 +416,13 @@ my_shout_open_device(AudioOutput *ao, AudioFormat &audio_format, if (!shout_connect(sd, error)) return false; - if (!encoder_open(sd->encoder, audio_format, error)) { + if (!sd->encoder->Open(audio_format, error)) { shout_close(sd->shout_conn); return false; } if (!write_page(sd, error)) { - encoder_close(sd->encoder); + sd->encoder->Close(); shout_close(sd->shout_conn); return false; } @@ -462,7 +463,7 @@ my_shout_pause(AudioOutput *ao) } static void -shout_tag_to_metadata(const Tag *tag, char *dest, size_t size) +shout_tag_to_metadata(const Tag &tag, char *dest, size_t size) { char artist[size]; char title[size]; @@ -470,7 +471,7 @@ shout_tag_to_metadata(const Tag *tag, char *dest, size_t size) artist[0] = 0; title[0] = 0; - for (const auto &item : *tag) { + for (const auto &item : tag) { switch (item.type) { case TAG_ARTIST: strncpy(artist, item.value, size); @@ -488,7 +489,7 @@ shout_tag_to_metadata(const Tag *tag, char *dest, size_t size) } static void my_shout_set_tag(AudioOutput *ao, - const Tag *tag) + const Tag &tag) { ShoutOutput *sd = (ShoutOutput *)ao; diff --git a/src/output/plugins/ShoutOutputPlugin.hxx b/src/output/plugins/ShoutOutputPlugin.hxx index 9f706fc3b..b103b3bf0 100644 --- a/src/output/plugins/ShoutOutputPlugin.hxx +++ b/src/output/plugins/ShoutOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/SolarisOutputPlugin.cxx b/src/output/plugins/SolarisOutputPlugin.cxx index 30745f97c..18c92d361 100644 --- a/src/output/plugins/SolarisOutputPlugin.cxx +++ b/src/output/plugins/SolarisOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -60,8 +60,8 @@ struct SolarisOutput { SolarisOutput() :base(solaris_output_plugin) {} - bool Initialize(const config_param ¶m, Error &error_r) { - return base.Configure(param, error_r); + bool Initialize(const ConfigBlock &block, Error &error_r) { + return base.Configure(block, error_r); } }; @@ -75,15 +75,15 @@ solaris_output_test_default_device(void) } static AudioOutput * -solaris_output_init(const config_param ¶m, Error &error_r) +solaris_output_init(const ConfigBlock &block, Error &error_r) { SolarisOutput *so = new SolarisOutput(); - if (!so->Initialize(param, error_r)) { + if (!so->Initialize(block, error_r)) { delete so; return nullptr; } - so->device = param.GetBlockValue("device", "/dev/audio"); + so->device = block.GetBlockValue("device", "/dev/audio"); return &so->base; } diff --git a/src/output/plugins/SolarisOutputPlugin.hxx b/src/output/plugins/SolarisOutputPlugin.hxx index 3f9ede7a6..f6f32504a 100644 --- a/src/output/plugins/SolarisOutputPlugin.hxx +++ b/src/output/plugins/SolarisOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/WinmmOutputPlugin.cxx b/src/output/plugins/WinmmOutputPlugin.cxx index e5c5a6f0c..35efb0f93 100644 --- a/src/output/plugins/WinmmOutputPlugin.cxx +++ b/src/output/plugins/WinmmOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,9 +22,11 @@ #include "../OutputAPI.hxx" #include "pcm/PcmBuffer.hxx" #include "mixer/MixerList.hxx" +#include "fs/AllocatedPath.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "util/Macros.hxx" +#include "util/StringUtil.hxx" #include <stdlib.h> #include <string.h> @@ -56,6 +58,18 @@ struct WinmmOutput { static constexpr Domain winmm_output_domain("winmm_output"); +static void +SetWaveOutError(Error &error, MMRESULT result, const char *prefix) +{ + char buffer[256]; + if (waveOutGetErrorTextA(result, buffer, + ARRAY_SIZE(buffer)) == MMSYSERR_NOERROR) + error.Format(winmm_output_domain, int(result), + "%s: %s", prefix, buffer); + else + error.Set(winmm_output_domain, int(result), prefix); +} + HWAVEOUT winmm_output_get_handle(WinmmOutput &output) { @@ -83,13 +97,23 @@ get_device_id(const char *device_name, UINT *device_id, Error &error) char *endptr; UINT id = strtoul(device_name, &endptr, 0); if (endptr > device_name && *endptr == 0) { - if (id >= numdevs) - goto fail; + if (id >= numdevs) { + error.Format(winmm_output_domain, + "device \"%s\" is not found", + device_name); + return false; + } + *device_id = id; return true; } /* check for device name */ + const AllocatedPath device_name_fs = + AllocatedPath::FromUTF8(device_name, error); + if (device_name_fs.IsNull()) + return false; + for (UINT i = 0; i < numdevs; i++) { WAVEOUTCAPS caps; MMRESULT result = waveOutGetDevCaps(i, &caps, sizeof(caps)); @@ -97,28 +121,27 @@ get_device_id(const char *device_name, UINT *device_id, Error &error) continue; /* szPname is only 32 chars long, so it is often truncated. Use partial match to work around this. */ - if (strstr(device_name, caps.szPname) == device_name) { + if (StringStartsWith(device_name_fs.c_str(), caps.szPname)) { *device_id = i; return true; } } -fail: error.Format(winmm_output_domain, "device \"%s\" is not found", device_name); return false; } static AudioOutput * -winmm_output_init(const config_param ¶m, Error &error) +winmm_output_init(const ConfigBlock &block, Error &error) { WinmmOutput *wo = new WinmmOutput(); - if (!wo->base.Configure(param, error)) { + if (!wo->base.Configure(block, error)) { delete wo; return nullptr; } - const char *device = param.GetBlockValue("device"); + const char *device = block.GetBlockValue("device"); if (!get_device_id(device, &wo->device_id, error)) { delete wo; return nullptr; @@ -179,7 +202,7 @@ winmm_output_open(AudioOutput *ao, AudioFormat &audio_format, (DWORD_PTR)wo->event, 0, CALLBACK_EVENT); if (result != MMSYSERR_NOERROR) { CloseHandle(wo->event); - error.Set(winmm_output_domain, "waveOutOpen() failed"); + SetWaveOutError(error, result, "waveOutOpen() failed"); return false; } @@ -225,8 +248,8 @@ winmm_set_buffer(WinmmOutput *wo, WinmmBuffer *buffer, MMRESULT result = waveOutPrepareHeader(wo->handle, &buffer->hdr, sizeof(buffer->hdr)); if (result != MMSYSERR_NOERROR) { - error.Set(winmm_output_domain, result, - "waveOutPrepareHeader() failed"); + SetWaveOutError(error, result, + "waveOutPrepareHeader() failed"); return false; } @@ -251,8 +274,8 @@ winmm_drain_buffer(WinmmOutput *wo, WinmmBuffer *buffer, if (result == MMSYSERR_NOERROR) return true; else if (result != WAVERR_STILLPLAYING) { - error.Set(winmm_output_domain, result, - "waveOutUnprepareHeader() failed"); + SetWaveOutError(error, result, + "waveOutUnprepareHeader() failed"); return false; } @@ -278,8 +301,7 @@ winmm_output_play(AudioOutput *ao, const void *chunk, size_t size, Error &error) if (result != MMSYSERR_NOERROR) { waveOutUnprepareHeader(wo->handle, &buffer->hdr, sizeof(buffer->hdr)); - error.Set(winmm_output_domain, result, - "waveOutWrite() failed"); + SetWaveOutError(error, result, "waveOutWrite() failed"); return 0; } diff --git a/src/output/plugins/WinmmOutputPlugin.hxx b/src/output/plugins/WinmmOutputPlugin.hxx index 50fae4f2f..6c8fcc627 100644 --- a/src/output/plugins/WinmmOutputPlugin.hxx +++ b/src/output/plugins/WinmmOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/httpd/HttpdClient.cxx b/src/output/plugins/httpd/HttpdClient.cxx index 3797c3d26..99210c1fd 100644 --- a/src/output/plugins/httpd/HttpdClient.cxx +++ b/src/output/plugins/httpd/HttpdClient.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,7 +23,7 @@ #include "util/ASCII.hxx" #include "Page.hxx" #include "IcyMetaDataServer.hxx" -#include "system/SocketError.hxx" +#include "net/SocketError.hxx" #include "Log.hxx" #include <assert.h> diff --git a/src/output/plugins/httpd/HttpdClient.hxx b/src/output/plugins/httpd/HttpdClient.hxx index f94f05769..6646ddf4c 100644 --- a/src/output/plugins/httpd/HttpdClient.hxx +++ b/src/output/plugins/httpd/HttpdClient.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,8 @@ #include "event/BufferedSocket.hxx" #include "Compiler.h" +#include <boost/intrusive/list.hpp> + #include <queue> #include <list> @@ -31,7 +33,9 @@ class HttpdOutput; class Page; -class HttpdClient final : BufferedSocket { +class HttpdClient final + : BufferedSocket, + public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>> { /** * The httpd output object this client is connected to. */ @@ -124,7 +128,7 @@ class HttpdClient final : BufferedSocket { public: /** * @param httpd the HTTP output device - * @param fd the socket file descriptor + * @param _fd the socket file descriptor */ HttpdClient(HttpdOutput &httpd, int _fd, EventLoop &_loop, bool _metadata_supported); diff --git a/src/output/plugins/httpd/HttpdInternal.hxx b/src/output/plugins/httpd/HttpdInternal.hxx index 20ff15e42..01498dfcd 100644 --- a/src/output/plugins/httpd/HttpdInternal.hxx +++ b/src/output/plugins/httpd/HttpdInternal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,7 @@ #ifndef MPD_OUTPUT_HTTPD_INTERNAL_H #define MPD_OUTPUT_HTTPD_INTERNAL_H +#include "HttpdClient.hxx" #include "output/Internal.hxx" #include "output/Timer.hxx" #include "thread/Mutex.hxx" @@ -33,16 +34,10 @@ #include "util/Cast.hxx" #include "Compiler.h" -#ifdef _LIBCPP_VERSION -/* can't use incomplete template arguments with libc++ */ -#include "HttpdClient.hxx" -#endif - -#include <forward_list> #include <queue> #include <list> -struct config_param; +struct ConfigBlock; class Error; class EventLoop; class ServerSocket; @@ -135,7 +130,8 @@ private: * A linked list containing all clients which are currently * connected. */ - std::forward_list<HttpdClient> clients; + boost::intrusive::list<HttpdClient, + boost::intrusive::constant_time_size<true>> clients; /** * A temporary buffer for the httpd_output_read_page() @@ -147,13 +143,13 @@ private: * The maximum and current number of clients connected * at the same time. */ - unsigned clients_max, clients_cnt; + unsigned clients_max; public: HttpdOutput(EventLoop &_loop); ~HttpdOutput(); -#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#if CLANG_OR_GCC_VERSION(4,7) constexpr #endif static HttpdOutput *Cast(AudioOutput *ao) { @@ -162,16 +158,16 @@ public: using DeferredMonitor::GetEventLoop; - bool Init(const config_param ¶m, Error &error); + bool Init(const ConfigBlock &block, Error &error); - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); - AudioOutput *InitAndConfigure(const config_param ¶m, + AudioOutput *InitAndConfigure(const ConfigBlock &block, Error &error) { - if (!Init(param, error)) + if (!Init(block, error)) return nullptr; - if (!Configure(param, error)) + if (!Configure(block, error)) return nullptr; return &base; @@ -250,7 +246,7 @@ public: bool EncodeAndPlay(const void *chunk, size_t size, Error &error); - void SendTag(const Tag *tag); + void SendTag(const Tag &tag); size_t Play(const void *chunk, size_t size, Error &error); @@ -259,8 +255,7 @@ public: private: virtual void RunDeferred() override; - virtual void OnAccept(int fd, const sockaddr &address, - size_t address_length, int uid) override; + void OnAccept(int fd, SocketAddress address, int uid) override; }; extern const class Domain httpd_output_domain; diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.cxx b/src/output/plugins/httpd/HttpdOutputPlugin.cxx index e3ba7727d..765a72d92 100644 --- a/src/output/plugins/httpd/HttpdOutputPlugin.cxx +++ b/src/output/plugins/httpd/HttpdOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,9 +22,11 @@ #include "HttpdInternal.hxx" #include "HttpdClient.hxx" #include "output/OutputAPI.hxx" +#include "encoder/EncoderInterface.hxx" #include "encoder/EncoderPlugin.hxx" #include "encoder/EncoderList.hxx" -#include "system/Resolver.hxx" +#include "net/SocketAddress.hxx" +#include "net/ToString.hxx" #include "Page.hxx" #include "IcyMetaDataServer.hxx" #include "system/fd_util.h" @@ -32,6 +34,7 @@ #include "event/Call.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" +#include "util/DeleteDisposer.hxx" #include "Log.hxx" #include <assert.h> @@ -63,7 +66,7 @@ HttpdOutput::~HttpdOutput() metadata->Unref(); if (encoder != nullptr) - encoder_finish(encoder); + encoder->Dispose(); } @@ -90,17 +93,17 @@ HttpdOutput::Unbind() } inline bool -HttpdOutput::Configure(const config_param ¶m, Error &error) +HttpdOutput::Configure(const ConfigBlock &block, Error &error) { /* read configuration */ - name = param.GetBlockValue("name", "Set name in config"); - genre = param.GetBlockValue("genre", "Set genre in config"); - website = param.GetBlockValue("website", "Set website in config"); + name = block.GetBlockValue("name", "Set name in config"); + genre = block.GetBlockValue("genre", "Set genre in config"); + website = block.GetBlockValue("website", "Set website in config"); - unsigned port = param.GetBlockValue("port", 8000u); + unsigned port = block.GetBlockValue("port", 8000u); const char *encoder_name = - param.GetBlockValue("encoder", "vorbis"); + block.GetBlockValue("encoder", "vorbis"); const auto encoder_plugin = encoder_plugin_get(encoder_name); if (encoder_plugin == nullptr) { error.Format(httpd_output_domain, @@ -108,11 +111,11 @@ HttpdOutput::Configure(const config_param ¶m, Error &error) return false; } - clients_max = param.GetBlockValue("max_clients", 0u); + clients_max = block.GetBlockValue("max_clients", 0u); /* set up bind_to_address */ - const char *bind_to_address = param.GetBlockValue("bind_to_address"); + const char *bind_to_address = block.GetBlockValue("bind_to_address"); bool success = bind_to_address != nullptr && strcmp(bind_to_address, "any") != 0 ? AddHost(bind_to_address, port, error) @@ -122,7 +125,7 @@ HttpdOutput::Configure(const config_param ¶m, Error &error) /* initialize encoder */ - encoder = encoder_init(*encoder_plugin, param, error); + encoder = encoder_init(*encoder_plugin, block, error); if (encoder == nullptr) return false; @@ -135,17 +138,17 @@ HttpdOutput::Configure(const config_param ¶m, Error &error) } inline bool -HttpdOutput::Init(const config_param ¶m, Error &error) +HttpdOutput::Init(const ConfigBlock &block, Error &error) { - return base.Configure(param, error); + return base.Configure(block, error); } static AudioOutput * -httpd_output_init(const config_param ¶m, Error &error) +httpd_output_init(const ConfigBlock &block, Error &error) { HttpdOutput *httpd = new HttpdOutput(io_thread_get()); - AudioOutput *result = httpd->InitAndConfigure(param, error); + AudioOutput *result = httpd->InitAndConfigure(block, error); if (result == nullptr) delete httpd; @@ -167,9 +170,9 @@ httpd_output_finish(AudioOutput *ao) inline void HttpdOutput::AddClient(int fd) { - clients.emplace_front(*this, fd, GetEventLoop(), - encoder->plugin.tag == nullptr); - ++clients_cnt; + auto *client = new HttpdClient(*this, fd, GetEventLoop(), + encoder->plugin.tag == nullptr); + clients.push_front(*client); /* pass metadata to client */ if (metadata != nullptr) @@ -200,16 +203,14 @@ HttpdOutput::RunDeferred() } void -HttpdOutput::OnAccept(int fd, const sockaddr &address, - size_t address_length, gcc_unused int uid) +HttpdOutput::OnAccept(int fd, SocketAddress address, gcc_unused int uid) { /* the listener socket has become readable - a client has connected */ #ifdef HAVE_LIBWRAP - if (address.sa_family != AF_UNIX) { - const auto hostaddr = sockaddr_to_string(&address, - address_length); + if (address.GetFamily() != AF_UNIX) { + const auto hostaddr = ToString(address); // TODO: shall we obtain the program name from argv[0]? const char *progname = "mpd"; @@ -229,14 +230,13 @@ HttpdOutput::OnAccept(int fd, const sockaddr &address, } #else (void)address; - (void)address_length; #endif /* HAVE_WRAP */ const ScopeLock protect(mutex); if (fd >= 0) { /* can we allow additional client */ - if (open && (clients_max == 0 || clients_cnt < clients_max)) + if (open && (clients_max == 0 || clients.size() < clients_max)) AddClient(fd); else close_socket(fd); @@ -294,7 +294,7 @@ httpd_output_disable(AudioOutput *ao) inline bool HttpdOutput::OpenEncoder(AudioFormat &audio_format, Error &error) { - if (!encoder_open(encoder, audio_format, error)) + if (!encoder->Open(audio_format, error)) return false; /* we have to remember the encoder header, i.e. the first @@ -320,7 +320,6 @@ HttpdOutput::Open(AudioFormat &audio_format, Error &error) /* initialize other attributes */ - clients_cnt = 0; timer = new Timer(audio_format); open = true; @@ -348,13 +347,13 @@ HttpdOutput::Close() delete timer; BlockingCall(GetEventLoop(), [this](){ - clients.clear(); + clients.clear_and_dispose(DeleteDisposer()); }); if (header != nullptr) header->Unref(); - encoder_close(encoder); + encoder->Close(); } static void @@ -369,17 +368,10 @@ httpd_output_close(AudioOutput *ao) void HttpdOutput::RemoveClient(HttpdClient &client) { - assert(clients_cnt > 0); + assert(!clients.empty()); - for (auto prev = clients.before_begin(), i = std::next(prev);; - prev = i, i = std::next(prev)) { - assert(i != clients.end()); - if (&*i == &client) { - clients.erase_after(prev); - clients_cnt--; - break; - } - } + clients.erase_and_dispose(clients.iterator_to(client), + DeleteDisposer()); } void @@ -499,10 +491,8 @@ httpd_output_pause(AudioOutput *ao) } inline void -HttpdOutput::SendTag(const Tag *tag) +HttpdOutput::SendTag(const Tag &tag) { - assert(tag != nullptr); - if (encoder->plugin.tag != nullptr) { /* embed encoder tags */ @@ -538,7 +528,7 @@ HttpdOutput::SendTag(const Tag *tag) TAG_NUM_OF_ITEM_TYPES }; - metadata = icy_server_metadata_page(*tag, &types[0]); + metadata = icy_server_metadata_page(tag, &types[0]); if (metadata != nullptr) { const ScopeLock protect(mutex); for (auto &client : clients) @@ -548,7 +538,7 @@ HttpdOutput::SendTag(const Tag *tag) } static void -httpd_output_tag(AudioOutput *ao, const Tag *tag) +httpd_output_tag(AudioOutput *ao, const Tag &tag) { HttpdOutput *httpd = HttpdOutput::Cast(ao); diff --git a/src/output/plugins/httpd/HttpdOutputPlugin.hxx b/src/output/plugins/httpd/HttpdOutputPlugin.hxx index df99e2b43..8280d9fb2 100644 --- a/src/output/plugins/httpd/HttpdOutputPlugin.hxx +++ b/src/output/plugins/httpd/HttpdOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/httpd/IcyMetaDataServer.cxx b/src/output/plugins/httpd/IcyMetaDataServer.cxx index 146df23d1..fe841ac11 100644 --- a/src/output/plugins/httpd/IcyMetaDataServer.cxx +++ b/src/output/plugins/httpd/IcyMetaDataServer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,8 +22,8 @@ #include "Page.hxx" #include "tag/Tag.hxx" #include "util/FormatString.hxx" - -#include <glib.h> +#include "util/StringUtil.hxx" +#include "util/Macros.hxx" #include <string.h> @@ -57,16 +57,13 @@ icy_server_metadata_header(const char *name, static char * icy_server_metadata_string(const char *stream_title, const char* stream_url) { - gchar *icy_metadata; - guint meta_length; - // The leading n is a placeholder for the length information - icy_metadata = FormatNew("nStreamTitle='%s';" - "StreamUrl='%s';", - stream_title, - stream_url); + char *icy_metadata = FormatNew("nStreamTitle='%s';" + "StreamUrl='%s';", + stream_title, + stream_url); - meta_length = strlen(icy_metadata); + size_t meta_length = strlen(icy_metadata); meta_length--; // subtract placeholder @@ -85,43 +82,30 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url) Page * icy_server_metadata_page(const Tag &tag, const TagType *types) { - const gchar *tag_items[TAG_NUM_OF_ITEM_TYPES]; - gint last_item, item; - guint position; - gchar *icy_string; - gchar stream_title[(1 + 255 - 28) * 16]; // Length + Metadata - - // "StreamTitle='';StreamUrl='';" - // = 4081 - 28 - stream_title[0] = '\0'; - - last_item = -1; + const char *tag_items[TAG_NUM_OF_ITEM_TYPES]; + int last_item = -1; while (*types != TAG_NUM_OF_ITEM_TYPES) { - const gchar *tag_item = tag.GetValue(*types++); + const char *tag_item = tag.GetValue(*types++); if (tag_item) tag_items[++last_item] = tag_item; } - position = item = 0; - while (position < sizeof(stream_title) && item <= last_item) { - gint length = 0; - - length = g_strlcpy(stream_title + position, - tag_items[item++], - sizeof(stream_title) - position); + int item = 0; - position += length; + // Length + Metadata - "StreamTitle='';StreamUrl='';" = 4081 - 28 + char stream_title[(1 + 255 - 28) * 16]; + char *p = stream_title, *const end = stream_title + ARRAY_SIZE(stream_title); + stream_title[0] = '\0'; - if (item <= last_item) { - length = g_strlcpy(stream_title + position, - " - ", - sizeof(stream_title) - position); + while (p < end && item <= last_item) { + p = CopyString(p, tag_items[item++], end - p); - position += length; - } + if (item <= last_item) + p = CopyString(p, " - ", end - p); } - icy_string = icy_server_metadata_string(stream_title, ""); + char *icy_string = icy_server_metadata_string(stream_title, ""); if (icy_string == nullptr) return nullptr; diff --git a/src/output/plugins/httpd/IcyMetaDataServer.hxx b/src/output/plugins/httpd/IcyMetaDataServer.hxx index 773b46641..38415e5bd 100644 --- a/src/output/plugins/httpd/IcyMetaDataServer.hxx +++ b/src/output/plugins/httpd/IcyMetaDataServer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/httpd/Page.cxx b/src/output/plugins/httpd/Page.cxx index e22134bbc..ff7036645 100644 --- a/src/output/plugins/httpd/Page.cxx +++ b/src/output/plugins/httpd/Page.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/httpd/Page.hxx b/src/output/plugins/httpd/Page.hxx index 95f35d06a..88b7c2d85 100644 --- a/src/output/plugins/httpd/Page.hxx +++ b/src/output/plugins/httpd/Page.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/output/plugins/sles/SlesOutputPlugin.cxx b/src/output/plugins/sles/SlesOutputPlugin.cxx index 85fd9f2f2..1e23cd2cc 100644 --- a/src/output/plugins/sles/SlesOutputPlugin.cxx +++ b/src/output/plugins/sles/SlesOutputPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,7 @@ #include "Play.hxx" #include "AndroidSimpleBufferQueue.hxx" #include "../../OutputAPI.hxx" +#include "../../Wrapper.hxx" #include "util/Macros.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -34,6 +35,8 @@ #include <SLES/OpenSLES_Android.h> class SlesOutput { + friend struct AudioOutputWrapper<SlesOutput>; + static constexpr unsigned N_BUFFERS = 3; static constexpr size_t BUFFER_SIZE = 65536; @@ -88,11 +91,13 @@ public: return &base; } - bool Initialize(const config_param ¶m, Error &error) { - return base.Configure(param, error); + bool Initialize(const ConfigBlock &block, Error &error) { + return base.Configure(block, error); } - bool Configure(const config_param ¶m, Error &error); + bool Configure(const ConfigBlock &block, Error &error); + + static SlesOutput *Create(const ConfigBlock &block, Error &error); bool Open(AudioFormat &audio_format, Error &error); void Close(); @@ -126,7 +131,7 @@ private: static constexpr Domain sles_domain("sles"); inline bool -SlesOutput::Configure(const config_param &, Error &) +SlesOutput::Configure(const ConfigBlock &, Error &) { return true; } @@ -441,99 +446,36 @@ sles_test_default_device() return true; } -static AudioOutput * -sles_output_init(const config_param ¶m, Error &error) +inline SlesOutput * +SlesOutput::Create(const ConfigBlock &block, Error &error) { SlesOutput *sles = new SlesOutput(); - if (!sles->Initialize(param, error) || - !sles->Configure(param, error)) { + if (!sles->Initialize(block, error) || + !sles->Configure(block, error)) { delete sles; return nullptr; } - return *sles; -} - -static void -sles_output_finish(AudioOutput *ao) -{ - SlesOutput *sles = (SlesOutput *)ao; - - delete sles; -} - -static bool -sles_output_open(AudioOutput *ao, AudioFormat &audio_format, Error &error) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - return sles.Open(audio_format, error); + return sles; } -static void -sles_output_close(AudioOutput *ao) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - sles.Close(); -} - -static unsigned -sles_output_delay(AudioOutput *ao) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - return sles.Delay(); -} - -static size_t -sles_output_play(AudioOutput *ao, const void *chunk, size_t size, - Error &error) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - return sles.Play(chunk, size, error); -} - -static void -sles_output_drain(AudioOutput *ao) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - sles.Drain(); -} - -static void -sles_output_cancel(AudioOutput *ao) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - sles.Cancel(); -} - -static bool -sles_output_pause(AudioOutput *ao) -{ - SlesOutput &sles = *(SlesOutput *)ao; - - return sles.Pause(); -} +typedef AudioOutputWrapper<SlesOutput> Wrapper; const struct AudioOutputPlugin sles_output_plugin = { "sles", sles_test_default_device, - sles_output_init, - sles_output_finish, + &Wrapper::Init, + &Wrapper::Finish, nullptr, nullptr, - sles_output_open, - sles_output_close, - sles_output_delay, + &Wrapper::Open, + &Wrapper::Close, + &Wrapper::Delay, nullptr, - sles_output_play, - sles_output_drain, - sles_output_cancel, - sles_output_pause, + &Wrapper::Play, + &Wrapper::Drain, + &Wrapper::Cancel, + &Wrapper::Pause, nullptr, }; diff --git a/src/output/plugins/sles/SlesOutputPlugin.hxx b/src/output/plugins/sles/SlesOutputPlugin.hxx index 5424dec2e..5a7595c24 100644 --- a/src/output/plugins/sles/SlesOutputPlugin.hxx +++ b/src/output/plugins/sles/SlesOutputPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/ChannelsConverter.cxx b/src/pcm/ChannelsConverter.cxx index 714613788..261af77ca 100644 --- a/src/pcm/ChannelsConverter.cxx +++ b/src/pcm/ChannelsConverter.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/ChannelsConverter.hxx b/src/pcm/ChannelsConverter.hxx index 1374f9f5d..aba230a86 100644 --- a/src/pcm/ChannelsConverter.hxx +++ b/src/pcm/ChannelsConverter.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/ConfiguredResampler.cxx b/src/pcm/ConfiguredResampler.cxx index f6aec3f95..30cb801c7 100644 --- a/src/pcm/ConfiguredResampler.cxx +++ b/src/pcm/ConfiguredResampler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,13 +23,15 @@ #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "config/ConfigError.hxx" +#include "config/Block.hxx" +#include "config/Param.hxx" #include "util/Error.hxx" -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE #include "LibsamplerateResampler.hxx" #endif -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR #include "SoxrResampler.hxx" #endif @@ -38,45 +40,130 @@ enum class SelectedResampler { FALLBACK, -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE LIBSAMPLERATE, #endif -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR SOXR, #endif }; static SelectedResampler selected_resampler = SelectedResampler::FALLBACK; -bool -pcm_resampler_global_init(Error &error) +static const ConfigBlock * +MakeResamplerDefaultConfig(ConfigBlock &block) { - const char *converter = - config_get_string(CONF_SAMPLERATE_CONVERTER, ""); + assert(block.IsEmpty()); + +#ifdef ENABLE_LIBSAMPLERATE + block.AddBlockParam("plugin", "libsamplerate"); +#elif defined(ENABLE_SOXR) + block.AddBlockParam("plugin", "soxr"); +#else + block.AddBlockParam("plugin", "internal"); +#endif + return █ +} - if (strcmp(converter, "internal") == 0) - return true; +/** + * Convert the old "samplerate_converter" setting to a new-style + * "resampler" block. + */ +static const ConfigBlock * +MigrateResamplerConfig(const config_param ¶m, ConfigBlock &block) +{ + assert(block.IsEmpty()); -#ifdef HAVE_SOXR - if (memcmp(converter, "soxr", 4) == 0) { - selected_resampler = SelectedResampler::SOXR; - return pcm_resample_soxr_global_init(converter, error); + block.line = param.line; + + const char *converter = param.value.c_str(); + if (*converter == 0 || strcmp(converter, "internal") == 0) { + block.AddBlockParam("plugin", "internal"); + return █ + } + +#ifdef ENABLE_SOXR + if (strcmp(converter, "soxr") == 0) { + block.AddBlockParam("plugin", "soxr"); + return █ } -#endif -#ifdef HAVE_LIBSAMPLERATE - selected_resampler = SelectedResampler::LIBSAMPLERATE; - return pcm_resample_lsr_global_init(converter, error); + if (memcmp(converter, "soxr ", 5) == 0) { + block.AddBlockParam("plugin", "soxr"); + block.AddBlockParam("quality", converter + 5); + return █ + } #endif - if (*converter == 0) - return true; + block.AddBlockParam("plugin", "libsamplerate"); + block.AddBlockParam("type", converter); + return █ +} + +static const ConfigBlock * +MigrateResamplerConfig(const config_param *param, ConfigBlock &buffer) +{ + assert(buffer.IsEmpty()); - error.Format(config_domain, - "The samplerate_converter '%s' is not available", - converter); - return false; + return param == nullptr + ? MakeResamplerDefaultConfig(buffer) + : MigrateResamplerConfig(*param, buffer); +} + +static const ConfigBlock * +GetResamplerConfig(ConfigBlock &buffer, Error &error) +{ + const auto *old_param = + config_get_param(ConfigOption::SAMPLERATE_CONVERTER); + const auto *block = config_get_block(ConfigBlockOption::RESAMPLER); + if (block == nullptr) + return MigrateResamplerConfig(old_param, buffer); + + if (old_param != nullptr) { + error.Format(config_domain, + "Cannot use both 'resampler' (line %d) and 'samplerate_converter' (line %d)", + block->line, old_param->line); + return nullptr; + } + + return block; +} + +bool +pcm_resampler_global_init(Error &error) +{ + ConfigBlock buffer; + const auto *block = GetResamplerConfig(buffer, error); + if (block == nullptr) + return false; + + const char *plugin_name = block->GetBlockValue("plugin"); + if (plugin_name == nullptr) { + error.Format(config_domain, + "'plugin' missing in line %d", block->line); + return false; + } + + if (strcmp(plugin_name, "internal") == 0) { + selected_resampler = SelectedResampler::FALLBACK; + return true; +#ifdef ENABLE_SOXR + } else if (strcmp(plugin_name, "soxr") == 0) { + selected_resampler = SelectedResampler::SOXR; + return pcm_resample_soxr_global_init(*block, error); +#endif +#ifdef ENABLE_LIBSAMPLERATE + } else if (strcmp(plugin_name, "libsamplerate") == 0) { + selected_resampler = SelectedResampler::LIBSAMPLERATE; + return pcm_resample_lsr_global_init(*block, error); +#endif + } else { + error.Format(config_domain, + "No such resampler plugin: %s", + plugin_name); + return false; + } } PcmResampler * @@ -86,12 +173,12 @@ pcm_resampler_create() case SelectedResampler::FALLBACK: return new FallbackPcmResampler(); -#ifdef HAVE_LIBSAMPLERATE +#ifdef ENABLE_LIBSAMPLERATE case SelectedResampler::LIBSAMPLERATE: return new LibsampleratePcmResampler(); #endif -#ifdef HAVE_SOXR +#ifdef ENABLE_SOXR case SelectedResampler::SOXR: return new SoxrPcmResampler(); #endif diff --git a/src/pcm/ConfiguredResampler.hxx b/src/pcm/ConfiguredResampler.hxx index 2b14b381e..090f2ae0a 100644 --- a/src/pcm/ConfiguredResampler.hxx +++ b/src/pcm/ConfiguredResampler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/Domain.cxx b/src/pcm/Domain.cxx index ecd5c22a4..8673e5a10 100644 --- a/src/pcm/Domain.cxx +++ b/src/pcm/Domain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/Domain.hxx b/src/pcm/Domain.hxx index 781d5c71b..47d5ef8b9 100644 --- a/src/pcm/Domain.hxx +++ b/src/pcm/Domain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/FallbackResampler.cxx b/src/pcm/FallbackResampler.cxx index bd3f20d86..74fbc29bd 100644 --- a/src/pcm/FallbackResampler.cxx +++ b/src/pcm/FallbackResampler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/FallbackResampler.hxx b/src/pcm/FallbackResampler.hxx index 38273f53f..d96b89d4f 100644 --- a/src/pcm/FallbackResampler.hxx +++ b/src/pcm/FallbackResampler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/FloatConvert.hxx b/src/pcm/FloatConvert.hxx index 93e867159..47fe8d65a 100644 --- a/src/pcm/FloatConvert.hxx +++ b/src/pcm/FloatConvert.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/FormatConverter.cxx b/src/pcm/FormatConverter.cxx index 8874e1b3c..28e585e70 100644 --- a/src/pcm/FormatConverter.cxx +++ b/src/pcm/FormatConverter.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/FormatConverter.hxx b/src/pcm/FormatConverter.hxx index 3d8b6fb75..a67fcd7d6 100644 --- a/src/pcm/FormatConverter.hxx +++ b/src/pcm/FormatConverter.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/GlueResampler.cxx b/src/pcm/GlueResampler.cxx index 0f5fe0271..3b1b61c3b 100644 --- a/src/pcm/GlueResampler.cxx +++ b/src/pcm/GlueResampler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/GlueResampler.hxx b/src/pcm/GlueResampler.hxx index aff07823e..7471d39b7 100644 --- a/src/pcm/GlueResampler.hxx +++ b/src/pcm/GlueResampler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/Interleave.cxx b/src/pcm/Interleave.cxx new file mode 100644 index 000000000..ed9fc125d --- /dev/null +++ b/src/pcm/Interleave.cxx @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2003-2015 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 "Interleave.hxx" + +#include <string.h> + +static void +GenericPcmInterleave(uint8_t *gcc_restrict dest, + ConstBuffer<const uint8_t *> src, + size_t n_frames, size_t sample_size) +{ + for (size_t frame = 0; frame < n_frames; ++frame) { + for (size_t channel = 0; channel < src.size; ++channel) { + memcpy(dest, src[channel] + frame * sample_size, + sample_size); + dest += sample_size; + } + } +} + +template<typename T> +static void +PcmInterleaveStereo(T *gcc_restrict dest, + const T *gcc_restrict src1, + const T *gcc_restrict src2, + size_t n_frames) +{ + for (size_t i = 0; i != n_frames; ++i) { + *dest++ = *src1++; + *dest++ = *src2++; + } +} + +template<typename T> +static void +PcmInterleaveT(T *gcc_restrict dest, + const ConstBuffer<const T *> src, + size_t n_frames) +{ + switch (src.size) { + case 2: + PcmInterleaveStereo(dest, src[0], src[1], n_frames); + return; + } + + for (const auto *s : src) { + auto *d = dest++; + + for (const auto *const s_end = s + n_frames; + s != s_end; ++s, d += src.size) + *d = *s; + } +} + +static void +PcmInterleave16(int16_t *gcc_restrict dest, + const ConstBuffer<const int16_t *> src, + size_t n_frames) +{ + PcmInterleaveT(dest, src, n_frames); +} + +void +PcmInterleave32(int32_t *gcc_restrict dest, + const ConstBuffer<const int32_t *> src, + size_t n_frames) +{ + PcmInterleaveT(dest, src, n_frames); +} + +void +PcmInterleave(void *gcc_restrict dest, + ConstBuffer<const void *> src, + size_t n_frames, size_t sample_size) +{ + switch (sample_size) { + case 2: + PcmInterleave16((int16_t *)dest, + ConstBuffer<const int16_t *>((const int16_t *const*)src.data, + src.size), + n_frames); + break; + + case 4: + PcmInterleave32((int32_t *)dest, + ConstBuffer<const int32_t *>((const int32_t *const*)src.data, + src.size), + n_frames); + break; + + default: + GenericPcmInterleave((uint8_t *)dest, + ConstBuffer<const uint8_t *>((const uint8_t *const*)src.data, + src.size), + n_frames, sample_size); + } +} diff --git a/src/pcm/Interleave.hxx b/src/pcm/Interleave.hxx new file mode 100644 index 000000000..3cb117d80 --- /dev/null +++ b/src/pcm/Interleave.hxx @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2003-2015 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_PCM_INTERLEAVE_HXX +#define MPD_PCM_INTERLEAVE_HXX + +#include "check.h" +#include "Compiler.h" +#include "util/ConstBuffer.hxx" + +#include <stdint.h> + +/** + * Interleave planar PCM samples from #src to #dest. + */ +void +PcmInterleave(void *gcc_restrict dest, ConstBuffer<const void *> src, + size_t n_frames, size_t sample_size); + +/** + * A variant of PcmInterleave() that assumes 32 bit samples (4 bytes + * per sample). + */ +void +PcmInterleave32(int32_t *gcc_restrict dest, ConstBuffer<const int32_t *> src, + size_t n_frames); + +static inline void +PcmInterleaveFloat(float *gcc_restrict dest, ConstBuffer<const float *> src, + size_t n_frames) +{ + PcmInterleave32((int32_t *)dest, + ConstBuffer<const int32_t *>((const int32_t *const*)src.data, + src.size), + n_frames); +} + +#endif diff --git a/src/pcm/LibsamplerateResampler.cxx b/src/pcm/LibsamplerateResampler.cxx index 8b22f1e32..cc6f3d43f 100644 --- a/src/pcm/LibsamplerateResampler.cxx +++ b/src/pcm/LibsamplerateResampler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,7 @@ #include "config.h" #include "LibsamplerateResampler.hxx" +#include "config/Block.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -63,8 +64,9 @@ lsr_parse_converter(const char *s) } bool -pcm_resample_lsr_global_init(const char *converter, Error &error) +pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error) { + const char *converter = block.GetBlockValue("type", "2"); if (!lsr_parse_converter(converter)) { error.Format(libsamplerate_domain, "unknown samplerate converter '%s'", converter); diff --git a/src/pcm/LibsamplerateResampler.hxx b/src/pcm/LibsamplerateResampler.hxx index 4f4e645e6..f19dc19eb 100644 --- a/src/pcm/LibsamplerateResampler.hxx +++ b/src/pcm/LibsamplerateResampler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,8 @@ #include <samplerate.h> +struct ConfigBlock; + /** * A resampler using libsamplerate. */ @@ -51,6 +53,6 @@ private: }; bool -pcm_resample_lsr_global_init(const char *converter, Error &error); +pcm_resample_lsr_global_init(const ConfigBlock &block, Error &error); #endif diff --git a/src/pcm/Neon.hxx b/src/pcm/Neon.hxx index 7109778ab..a2a92eea6 100644 --- a/src/pcm/Neon.hxx +++ b/src/pcm/Neon.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmBuffer.cxx b/src/pcm/PcmBuffer.cxx index 7bba2de47..e767872bd 100644 --- a/src/pcm/PcmBuffer.cxx +++ b/src/pcm/PcmBuffer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmBuffer.hxx b/src/pcm/PcmBuffer.hxx index f56a85985..eafdc649e 100644 --- a/src/pcm/PcmBuffer.hxx +++ b/src/pcm/PcmBuffer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmChannels.cxx b/src/pcm/PcmChannels.cxx index 276f31045..5cf730e6c 100644 --- a/src/pcm/PcmChannels.cxx +++ b/src/pcm/PcmChannels.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmChannels.hxx b/src/pcm/PcmChannels.hxx index 6ad093c3b..eb6aa4828 100644 --- a/src/pcm/PcmChannels.hxx +++ b/src/pcm/PcmChannels.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx index 438566759..ccc45c246 100644 --- a/src/pcm/PcmConvert.cxx +++ b/src/pcm/PcmConvert.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmConvert.hxx b/src/pcm/PcmConvert.hxx index 26ab02923..1f5e41bc6 100644 --- a/src/pcm/PcmConvert.hxx +++ b/src/pcm/PcmConvert.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -74,11 +74,8 @@ public: /** * Converts PCM data between two audio formats. * - * @param src_format the source audio format * @param src the source PCM buffer - * @param dest_format the requested destination audio format - * @param error_r location to store the error occurring, or nullptr to - * ignore errors + * @param error location to store the error occurring * @return the destination buffer, or nullptr on error */ ConstBuffer<void> Convert(ConstBuffer<void> src, Error &error); diff --git a/src/pcm/PcmDither.cxx b/src/pcm/PcmDither.cxx index 7b2a9e900..25252458b 100644 --- a/src/pcm/PcmDither.cxx +++ b/src/pcm/PcmDither.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmDither.hxx b/src/pcm/PcmDither.hxx index 54b0f7315..491f22601 100644 --- a/src/pcm/PcmDither.hxx +++ b/src/pcm/PcmDither.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,9 +36,9 @@ public: * Shift the given sample by #SBITS-#DBITS to the right, and * apply dithering. * - * @param ST the input sample type - * @param SBITS the input bit width - * @param DBITS the output bit width + * @tparam ST the input sample type + * @tparam SBITS the input bit width + * @tparam DBITS the output bit width * @param sample the input sample value */ template<typename ST, unsigned SBITS, unsigned DBITS> diff --git a/src/pcm/PcmDop.cxx b/src/pcm/PcmDop.cxx index b2096d9e4..e60c6d14d 100644 --- a/src/pcm/PcmDop.cxx +++ b/src/pcm/PcmDop.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmDop.hxx b/src/pcm/PcmDop.hxx index 03161c456..82c045911 100644 --- a/src/pcm/PcmDop.hxx +++ b/src/pcm/PcmDop.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmDsd.cxx b/src/pcm/PcmDsd.cxx index 53d26d480..f27c63f33 100644 --- a/src/pcm/PcmDsd.cxx +++ b/src/pcm/PcmDsd.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmDsd.hxx b/src/pcm/PcmDsd.hxx index e3e3a3cb1..89654ebe6 100644 --- a/src/pcm/PcmDsd.hxx +++ b/src/pcm/PcmDsd.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmExport.cxx b/src/pcm/PcmExport.cxx index ef099ba71..af2eb7d9f 100644 --- a/src/pcm/PcmExport.cxx +++ b/src/pcm/PcmExport.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmExport.hxx b/src/pcm/PcmExport.hxx index b99a35835..7265ca07d 100644 --- a/src/pcm/PcmExport.hxx +++ b/src/pcm/PcmExport.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -86,7 +86,7 @@ struct PcmExport { uint8_t reverse_endian; /** - * Open the #pcm_export_state object. + * Open the object. * * There is no "close" method. This function may be called multiple * times to reuse the object. diff --git a/src/pcm/PcmFormat.cxx b/src/pcm/PcmFormat.cxx index 4cabc05a0..a70a38982 100644 --- a/src/pcm/PcmFormat.cxx +++ b/src/pcm/PcmFormat.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmFormat.hxx b/src/pcm/PcmFormat.hxx index da182e771..9d15011a7 100644 --- a/src/pcm/PcmFormat.hxx +++ b/src/pcm/PcmFormat.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,9 +33,8 @@ class PcmDither; * Converts PCM samples to 16 bit. If the source format is 24 bit, * then dithering is applied. * - * @param buffer a PcmBuffer object - * @param dither a pcm_dither object for 24-to-16 conversion - * @param bits the number of in the source buffer + * @param buffer a #PcmBuffer object + * @param dither a #PcmDither object for 24-to-16 conversion * @param src the source PCM buffer * @return the destination buffer */ @@ -47,8 +46,7 @@ pcm_convert_to_16(PcmBuffer &buffer, PcmDither &dither, /** * Converts PCM samples to 24 bit (32 bit alignment). * - * @param buffer a PcmBuffer object - * @param bits the number of in the source buffer + * @param buffer a #PcmBuffer object * @param src the source PCM buffer * @return the destination buffer */ @@ -60,8 +58,7 @@ pcm_convert_to_24(PcmBuffer &buffer, /** * Converts PCM samples to 32 bit. * - * @param buffer a PcmBuffer object - * @param bits the number of in the source buffer + * @param buffer a #PcmBuffer object * @param src the source PCM buffer * @return the destination buffer */ @@ -73,11 +70,8 @@ pcm_convert_to_32(PcmBuffer &buffer, /** * Converts PCM samples to 32 bit floating point. * - * @param buffer a PcmBuffer object - * @param bits the number of in the source buffer + * @param buffer a #PcmBuffer object * @param src the source PCM buffer - * @param src_size the size of #src in bytes - * @param dest_size_r returns the number of bytes of the destination buffer * @return the destination buffer */ gcc_pure diff --git a/src/pcm/PcmMix.cxx b/src/pcm/PcmMix.cxx index d21b5f04b..b67a4ec24 100644 --- a/src/pcm/PcmMix.cxx +++ b/src/pcm/PcmMix.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmMix.hxx b/src/pcm/PcmMix.hxx index 4e22a33f1..a906dc402 100644 --- a/src/pcm/PcmMix.hxx +++ b/src/pcm/PcmMix.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmPack.cxx b/src/pcm/PcmPack.cxx index 7a3379ad0..ef4406b82 100644 --- a/src/pcm/PcmPack.cxx +++ b/src/pcm/PcmPack.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmPack.hxx b/src/pcm/PcmPack.hxx index 271a3cd25..e05601986 100644 --- a/src/pcm/PcmPack.hxx +++ b/src/pcm/PcmPack.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -35,7 +35,6 @@ * * @param dest the destination buffer (array of triples) * @param src the source buffer - * @param num_samples the number of samples to convert */ void pcm_pack_24(uint8_t *dest, const int32_t *src, const int32_t *src_end); @@ -46,7 +45,6 @@ pcm_pack_24(uint8_t *dest, const int32_t *src, const int32_t *src_end); * * @param dest the destination buffer * @param src the source buffer (array of triples) - * @param num_samples the number of samples to convert */ void pcm_unpack_24(int32_t *dest, const uint8_t *src, const uint8_t *src_end); diff --git a/src/pcm/PcmPrng.hxx b/src/pcm/PcmPrng.hxx index 5233caba6..38b48de7f 100644 --- a/src/pcm/PcmPrng.hxx +++ b/src/pcm/PcmPrng.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/PcmUtils.hxx b/src/pcm/PcmUtils.hxx index 23870a729..9ea9cf3b3 100644 --- a/src/pcm/PcmUtils.hxx +++ b/src/pcm/PcmUtils.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/Resampler.hxx b/src/pcm/Resampler.hxx index 9b6ccbbc7..75f91b6ab 100644 --- a/src/pcm/Resampler.hxx +++ b/src/pcm/Resampler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -58,10 +58,7 @@ public: * Resamples a block of PCM data. * * @param src the input buffer - * @param src_size the size of #src_buffer in bytes - * @param dest_size_r the size of the returned buffer - * @param error location to store the error occurring, or nullptr - * to ignore errors. + * @param error location to store the error occurring * @return the destination buffer on success (will be * invalidated by filter_close() or filter_filter()), nullptr on * error diff --git a/src/pcm/ShiftConvert.hxx b/src/pcm/ShiftConvert.hxx index 92f96b7ba..e678abc56 100644 --- a/src/pcm/ShiftConvert.hxx +++ b/src/pcm/ShiftConvert.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/SoxrResampler.cxx b/src/pcm/SoxrResampler.cxx index b9d6fc099..f335e92e6 100644 --- a/src/pcm/SoxrResampler.cxx +++ b/src/pcm/SoxrResampler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "config.h" #include "SoxrResampler.hxx" #include "AudioFormat.hxx" +#include "config/Block.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" @@ -32,68 +33,76 @@ static constexpr Domain soxr_domain("soxr"); -static unsigned long soxr_quality_recipe = SOXR_HQ; +static constexpr unsigned long SOXR_DEFAULT_RECIPE = SOXR_HQ; +/** + * Special value for "invalid argument". + */ +static constexpr unsigned long SOXR_INVALID_RECIPE = -1; + +static soxr_quality_spec_t soxr_quality; +static soxr_runtime_spec_t soxr_runtime; + +static constexpr struct { + unsigned long recipe; + const char *name; +} soxr_quality_table[] = { + { SOXR_VHQ, "very high" }, + { SOXR_HQ, "high" }, + { SOXR_MQ, "medium" }, + { SOXR_LQ, "low" }, + { SOXR_QQ, "quick" }, + { SOXR_INVALID_RECIPE, nullptr } +}; + +gcc_const static const char * soxr_quality_name(unsigned long recipe) { - switch (recipe) { - case SOXR_VHQ: - return "Very High Quality"; - case SOXR_HQ: - return "High Quality"; - case SOXR_MQ: - return "Medium Quality"; - case SOXR_LQ: - return "Low Quality"; - case SOXR_QQ: - return "Quick"; - } + for (const auto *i = soxr_quality_table;; ++i) { + assert(i->name != nullptr); - gcc_unreachable(); + if (i->recipe == recipe) + return i->name; + } } -static bool -soxr_parse_converter(const char *converter) +gcc_pure +static unsigned long +soxr_parse_quality(const char *quality) { - assert(converter != nullptr); - - assert(memcmp(converter, "soxr", 4) == 0); - if (converter[4] == '\0') - return true; - if (converter[4] != ' ') - return false; + if (quality == nullptr) + return SOXR_DEFAULT_RECIPE; - // converter example is "soxr very high", we want the "very high" part - const char *quality = converter + 5; - if (strcmp(quality, "very high") == 0) - soxr_quality_recipe = SOXR_VHQ; - else if (strcmp(quality, "high") == 0) - soxr_quality_recipe = SOXR_HQ; - else if (strcmp(quality, "medium") == 0) - soxr_quality_recipe = SOXR_MQ; - else if (strcmp(quality, "low") == 0) - soxr_quality_recipe = SOXR_LQ; - else if (strcmp(quality, "quick") == 0) - soxr_quality_recipe = SOXR_QQ; - else - return false; + for (const auto *i = soxr_quality_table; i->name != nullptr; ++i) + if (strcmp(i->name, quality) == 0) + return i->recipe; - return true; + return SOXR_INVALID_RECIPE; } bool -pcm_resample_soxr_global_init(const char *converter, Error &error) +pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error) { - if (!soxr_parse_converter(converter)) { + const char *quality_string = block.GetBlockValue("quality"); + unsigned long recipe = soxr_parse_quality(quality_string); + if (recipe == SOXR_INVALID_RECIPE) { + assert(quality_string != nullptr); + error.Format(soxr_domain, - "unknown samplerate converter '%s'", converter); + "unknown quality setting '%s' in line %d", + quality_string, block.line); return false; } + soxr_quality = soxr_quality_spec(recipe, 0); + FormatDebug(soxr_domain, "soxr converter '%s'", - soxr_quality_name(soxr_quality_recipe)); + soxr_quality_name(recipe)); + + const unsigned n_threads = block.GetBlockValue("threads", 1); + soxr_runtime = soxr_runtime_spec(n_threads); return true; } @@ -106,10 +115,9 @@ SoxrPcmResampler::Open(AudioFormat &af, unsigned new_sample_rate, assert(audio_valid_sample_rate(new_sample_rate)); soxr_error_t e; - soxr_quality_spec_t quality = soxr_quality_spec(soxr_quality_recipe, 0); soxr = soxr_create(af.sample_rate, new_sample_rate, af.channels, &e, - nullptr, &quality, nullptr); + nullptr, &soxr_quality, &soxr_runtime); if (soxr == nullptr) { error.Format(soxr_domain, "soxr initialization has failed: %s", e); diff --git a/src/pcm/SoxrResampler.hxx b/src/pcm/SoxrResampler.hxx index e4cba4a64..6c31ca45a 100644 --- a/src/pcm/SoxrResampler.hxx +++ b/src/pcm/SoxrResampler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include "Compiler.h" struct AudioFormat; +struct ConfigBlock; /** * A resampler using soxr. @@ -46,6 +47,6 @@ public: }; bool -pcm_resample_soxr_global_init(const char *converter, Error &error); +pcm_resample_soxr_global_init(const ConfigBlock &block, Error &error); #endif diff --git a/src/pcm/Traits.hxx b/src/pcm/Traits.hxx index 97259ac73..3d124ea2f 100644 --- a/src/pcm/Traits.hxx +++ b/src/pcm/Traits.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/Volume.cxx b/src/pcm/Volume.cxx index b12d8fd41..86dd8bd71 100644 --- a/src/pcm/Volume.cxx +++ b/src/pcm/Volume.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/pcm/Volume.hxx b/src/pcm/Volume.hxx index a156fc72e..5d51343b3 100644 --- a/src/pcm/Volume.hxx +++ b/src/pcm/Volume.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlayerControl.cxx b/src/player/Control.cxx index 4f1c3d2ac..d7352ad57 100644 --- a/src/PlayerControl.cxx +++ b/src/player/Control.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,7 +18,7 @@ */ #include "config.h" -#include "PlayerControl.hxx" +#include "Control.hxx" #include "Idle.hxx" #include "DetachedSong.hxx" diff --git a/src/PlayerControl.hxx b/src/player/Control.hxx index 4d06a1827..a2807a9a1 100644 --- a/src/PlayerControl.hxx +++ b/src/player/Control.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/CrossFade.cxx b/src/player/CrossFade.cxx index e3cc95b0d..6d7b41440 100644 --- a/src/CrossFade.cxx +++ b/src/player/CrossFade.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/CrossFade.hxx b/src/player/CrossFade.hxx index 81e96e8d3..672abb718 100644 --- a/src/CrossFade.hxx +++ b/src/player/CrossFade.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlayerListener.hxx b/src/player/Listener.hxx index 06f00a4f5..e10f2547b 100644 --- a/src/PlayerListener.hxx +++ b/src/player/Listener.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/PlayerThread.cxx b/src/player/Thread.cxx index eeebcdb96..60e253f4c 100644 --- a/src/PlayerThread.cxx +++ b/src/player/Thread.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,8 +18,8 @@ */ #include "config.h" -#include "PlayerThread.hxx" -#include "PlayerListener.hxx" +#include "Thread.hxx" +#include "Listener.hxx" #include "decoder/DecoderThread.hxx" #include "decoder/DecoderControl.hxx" #include "MusicPipe.hxx" @@ -28,7 +28,7 @@ #include "DetachedSong.hxx" #include "system/FatalError.hxx" #include "CrossFade.hxx" -#include "PlayerControl.hxx" +#include "Control.hxx" #include "output/MultipleOutputs.hxx" #include "tag/Tag.hxx" #include "Idle.hxx" diff --git a/src/PlayerThread.hxx b/src/player/Thread.hxx index 537e38399..fc6ea4364 100644 --- a/src/PlayerThread.hxx +++ b/src/player/Thread.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ * audio outputs via audio_output_all_play(). * * It is controlled by the main thread (the playlist code), see - * PlayerControl.hxx. The playlist enqueues new songs into the player + * Control.hxx. The playlist enqueues new songs into the player * thread and sends it commands. * * The player thread itself does not do any I/O. It synchronizes with diff --git a/src/playlist/CloseSongEnumerator.cxx b/src/playlist/CloseSongEnumerator.cxx index 2dddef823..6a95fd66e 100644 --- a/src/playlist/CloseSongEnumerator.cxx +++ b/src/playlist/CloseSongEnumerator.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/CloseSongEnumerator.hxx b/src/playlist/CloseSongEnumerator.hxx index 17f015394..f8e352f9b 100644 --- a/src/playlist/CloseSongEnumerator.hxx +++ b/src/playlist/CloseSongEnumerator.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/MemorySongEnumerator.cxx b/src/playlist/MemorySongEnumerator.cxx index c3127c2bf..e34a8d628 100644 --- a/src/playlist/MemorySongEnumerator.cxx +++ b/src/playlist/MemorySongEnumerator.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/MemorySongEnumerator.hxx b/src/playlist/MemorySongEnumerator.hxx index d1259f011..5a1493810 100644 --- a/src/playlist/MemorySongEnumerator.hxx +++ b/src/playlist/MemorySongEnumerator.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistAny.cxx b/src/playlist/PlaylistAny.cxx index 7093fb99a..be59d6b0c 100644 --- a/src/playlist/PlaylistAny.cxx +++ b/src/playlist/PlaylistAny.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistAny.hxx b/src/playlist/PlaylistAny.hxx index 23b0075b6..ca9bf662d 100644 --- a/src/playlist/PlaylistAny.hxx +++ b/src/playlist/PlaylistAny.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistMapper.cxx b/src/playlist/PlaylistMapper.cxx index 042a39d34..dbac3ccc7 100644 --- a/src/playlist/PlaylistMapper.cxx +++ b/src/playlist/PlaylistMapper.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistMapper.hxx b/src/playlist/PlaylistMapper.hxx index 29ce45083..e5309d649 100644 --- a/src/playlist/PlaylistMapper.hxx +++ b/src/playlist/PlaylistMapper.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistPlugin.hxx b/src/playlist/PlaylistPlugin.hxx index fd779ad8d..8d232d2cc 100644 --- a/src/playlist/PlaylistPlugin.hxx +++ b/src/playlist/PlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #ifndef MPD_PLAYLIST_PLUGIN_HXX #define MPD_PLAYLIST_PLUGIN_HXX -struct config_param; +struct ConfigBlock; class InputStream; struct Tag; class Mutex; @@ -33,18 +33,18 @@ struct playlist_plugin { /** * Initialize the plugin. Optional method. * - * @param param a configuration block for this plugin, or nullptr + * @param block a configuration block for this plugin, or nullptr * if none is configured * @return true if the plugin was initialized successfully, * false if the plugin is not available */ - bool (*init)(const config_param ¶m); + bool (*init)(const ConfigBlock &block); /** * Deinitialize a plugin which was initialized successfully. * Optional method. */ - void (*finish)(void); + void (*finish)(); /** * Opens the playlist on the specified URI. This URI has @@ -68,17 +68,17 @@ struct playlist_plugin { /** * Initialize a plugin. * - * @param param a configuration block for this plugin, or nullptr if none + * @param block a configuration block for this plugin, or nullptr if none * is configured * @return true if the plugin was initialized successfully, false if * the plugin is not available */ static inline bool playlist_plugin_init(const struct playlist_plugin *plugin, - const config_param ¶m) + const ConfigBlock &block) { return plugin->init != nullptr - ? plugin->init(param) + ? plugin->init(block) : true; } diff --git a/src/playlist/PlaylistQueue.cxx b/src/playlist/PlaylistQueue.cxx index b10a26172..b6f85f586 100644 --- a/src/playlist/PlaylistQueue.cxx +++ b/src/playlist/PlaylistQueue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistQueue.hxx b/src/playlist/PlaylistQueue.hxx index 28eb86fcc..16bafaecf 100644 --- a/src/playlist/PlaylistQueue.hxx +++ b/src/playlist/PlaylistQueue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx index 600f32b31..2156414be 100644 --- a/src/playlist/PlaylistRegistry.cxx +++ b/src/playlist/PlaylistRegistry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include "plugins/PlsPlaylistPlugin.hxx" #include "plugins/AsxPlaylistPlugin.hxx" #include "plugins/RssPlaylistPlugin.hxx" +#include "plugins/FlacPlaylistPlugin.hxx" #include "plugins/CuePlaylistPlugin.hxx" #include "plugins/EmbeddedCuePlaylistPlugin.hxx" #include "input/InputStream.hxx" @@ -35,7 +36,7 @@ #include "util/Error.hxx" #include "util/Macros.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "Log.hxx" #include <assert.h> @@ -44,11 +45,8 @@ const struct playlist_plugin *const playlist_plugins[] = { &extm3u_playlist_plugin, &m3u_playlist_plugin, -#ifdef HAVE_GLIB - // TODO: enable without GLib &pls_playlist_plugin, -#endif -#ifdef HAVE_EXPAT +#ifdef ENABLE_EXPAT &xspf_playlist_plugin, &asx_playlist_plugin, &rss_playlist_plugin, @@ -56,8 +54,13 @@ const struct playlist_plugin *const playlist_plugins[] = { #ifdef ENABLE_SOUNDCLOUD &soundcloud_playlist_plugin, #endif +#ifdef ENABLE_FLAC + &flac_playlist_plugin, +#endif +#ifdef ENABLE_CUE &cue_playlist_plugin, &embcue_playlist_plugin, +#endif nullptr }; @@ -74,13 +77,13 @@ static bool playlist_plugins_enabled[n_playlist_plugins]; void playlist_list_global_init(void) { - const config_param empty; + const ConfigBlock empty; for (unsigned i = 0; playlist_plugins[i] != nullptr; ++i) { const struct playlist_plugin *plugin = playlist_plugins[i]; - const struct config_param *param = - config_find_block(CONF_PLAYLIST_PLUGIN, "name", - plugin->name); + const auto *param = + config_find_block(ConfigBlockOption::PLAYLIST_PLUGIN, + "name", plugin->name); if (param == nullptr) param = ∅ else if (!param->GetBlockValue("enabled", true)) diff --git a/src/playlist/PlaylistRegistry.hxx b/src/playlist/PlaylistRegistry.hxx index 7ce559baa..09e842b13 100644 --- a/src/playlist/PlaylistRegistry.hxx +++ b/src/playlist/PlaylistRegistry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -37,13 +37,13 @@ extern const struct playlist_plugin *const playlist_plugins[]; * Initializes all playlist plugins. */ void -playlist_list_global_init(void); +playlist_list_global_init(); /** * Deinitializes all playlist plugins. */ void -playlist_list_global_finish(void); +playlist_list_global_finish(); /** * Opens a playlist by its URI. @@ -57,7 +57,7 @@ playlist_list_open_stream_suffix(InputStream &is, const char *suffix); /** * Opens a playlist from an input stream. * - * @param is an #input_stream object which is open and ready + * @param is an #InputStream object which is open and ready * @param uri optional URI which was used to open the stream; may be * used to select the appropriate playlist plugin */ diff --git a/src/playlist/PlaylistSong.cxx b/src/playlist/PlaylistSong.cxx index 3603c1add..72f913418 100644 --- a/src/playlist/PlaylistSong.cxx +++ b/src/playlist/PlaylistSong.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistSong.hxx b/src/playlist/PlaylistSong.hxx index 278df46a8..0674c02c9 100644 --- a/src/playlist/PlaylistSong.hxx +++ b/src/playlist/PlaylistSong.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/PlaylistStream.cxx b/src/playlist/PlaylistStream.cxx index 074f39d66..99e1c3182 100644 --- a/src/playlist/PlaylistStream.cxx +++ b/src/playlist/PlaylistStream.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -35,8 +35,12 @@ playlist_open_path_suffix(Path path, Mutex &mutex, Cond &cond) { assert(!path.IsNull()); - const char *suffix = uri_get_suffix(path.c_str()); - if (suffix == nullptr || !playlist_suffix_supported(suffix)) + const auto *suffix = path.GetSuffix(); + if (suffix == nullptr) + return nullptr; + + const auto suffix_utf8 = Path::FromFS(suffix).ToUTF8(); + if (!playlist_suffix_supported(suffix_utf8.c_str())) return nullptr; Error error; @@ -46,7 +50,8 @@ playlist_open_path_suffix(Path path, Mutex &mutex, Cond &cond) return nullptr; } - auto playlist = playlist_list_open_stream_suffix(*is, suffix); + auto playlist = playlist_list_open_stream_suffix(*is, + suffix_utf8.c_str()); if (playlist != nullptr) playlist = new CloseSongEnumerator(playlist, is); else diff --git a/src/playlist/PlaylistStream.hxx b/src/playlist/PlaylistStream.hxx index c07ae0b09..f9bba7722 100644 --- a/src/playlist/PlaylistStream.hxx +++ b/src/playlist/PlaylistStream.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -31,8 +31,6 @@ class Path; * Opens a playlist from a local file. * * @param path the path of the playlist file - * @param is_r on success, an input_stream object is returned here, - * which must be closed after the playlist_provider object is freed * @return a playlist, or nullptr on error */ gcc_nonnull_all diff --git a/src/playlist/Print.cxx b/src/playlist/Print.cxx index 8f743f56d..13e45d160 100644 --- a/src/playlist/Print.cxx +++ b/src/playlist/Print.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,48 +28,51 @@ #include "fs/Traits.hxx" #include "thread/Mutex.hxx" #include "thread/Cond.hxx" -#include "client/Client.hxx" +#include "Partition.hxx" +#include "Instance.hxx" static void -playlist_provider_print(Client &client, const char *uri, +playlist_provider_print(Response &r, Partition &partition, + const SongLoader &loader, + const char *uri, SongEnumerator &e, bool detail) { const std::string base_uri = uri != nullptr ? PathTraitsUTF8::GetParent(uri) : std::string("."); - const SongLoader loader(client); - DetachedSong *song; while ((song = e.NextSong()) != nullptr) { if (playlist_check_translate_song(*song, base_uri.c_str(), loader) && detail) - song_print_info(client, *song); + song_print_info(r, partition, *song); else /* fallback if no detail was requested or no detail was available */ - song_print_uri(client, *song); + song_print_uri(r, partition, *song); delete song; } } bool -playlist_file_print(Client &client, const char *uri, bool detail) +playlist_file_print(Response &r, Partition &partition, + const SongLoader &loader, + const char *uri, bool detail) { Mutex mutex; Cond cond; SongEnumerator *playlist = playlist_open_any(uri, #ifdef ENABLE_DATABASE - client.GetStorage(), + partition.instance.storage, #endif mutex, cond); if (playlist == nullptr) return false; - playlist_provider_print(client, uri, *playlist, detail); + playlist_provider_print(r, partition, loader, uri, *playlist, detail); delete playlist; return true; } diff --git a/src/playlist/Print.hxx b/src/playlist/Print.hxx index c2fff5475..3b356d4ce 100644 --- a/src/playlist/Print.hxx +++ b/src/playlist/Print.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,17 +20,20 @@ #ifndef MPD_PLAYLIST__PRINT_HXX #define MPD_PLAYLIST__PRINT_HXX -class Client; +class Response; +class SongLoader; +struct Partition; /** * Send the playlist file to the client. * - * @param client the client which requested the playlist * @param uri the URI of the playlist file in UTF-8 encoding * @param detail true if all details should be printed * @return true on success, false if the playlist does not exist */ bool -playlist_file_print(Client &client, const char *uri, bool detail); +playlist_file_print(Response &r, Partition &partition, + const SongLoader &loader, + const char *uri, bool detail); #endif diff --git a/src/playlist/SongEnumerator.hxx b/src/playlist/SongEnumerator.hxx index 75295add1..0f6997785 100644 --- a/src/playlist/SongEnumerator.hxx +++ b/src/playlist/SongEnumerator.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/cue/CueParser.cxx b/src/playlist/cue/CueParser.cxx index 372c90b78..81797fe28 100644 --- a/src/playlist/cue/CueParser.cxx +++ b/src/playlist/cue/CueParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -250,7 +250,6 @@ CueParser::Feed2(char *p) song_tag = header_tag; song_tag.AddItem(TAG_TRACK, nr); - last_updated = false; } else if (state == IGNORE_TRACK) { return; } else if (state == TRACK && strcmp(command, "INDEX") == 0) { @@ -266,13 +265,12 @@ CueParser::Feed2(char *p) if (position_ms < 0) return; - if (!last_updated && previous != nullptr && - previous->GetStartTime().ToMS() < (unsigned)position_ms) { - last_updated = true; + if (previous != nullptr && previous->GetStartTime().ToMS() < (unsigned)position_ms) previous->SetEndTime(SongTime::FromMS(position_ms)); - } current->SetStartTime(SongTime::FromMS(position_ms)); + if(strcmp(nr, "00") != 0 || previous == nullptr) + state = IGNORE_TRACK; } } diff --git a/src/playlist/cue/CueParser.hxx b/src/playlist/cue/CueParser.hxx index 7e040169b..925f1234c 100644 --- a/src/playlist/cue/CueParser.hxx +++ b/src/playlist/cue/CueParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -89,12 +89,6 @@ class CueParser { DetachedSong *finished; /** - * Set to true after previous.end_time has been updated to the - * start time of the current song. - */ - bool last_updated; - - /** * Tracks whether cue_parser_finish() has been called. If * true, then all remaining (partial) results will be * delivered by cue_parser_get(). diff --git a/src/playlist/plugins/AsxPlaylistPlugin.cxx b/src/playlist/plugins/AsxPlaylistPlugin.cxx index 3185a8144..1a0334237 100644 --- a/src/playlist/plugins/AsxPlaylistPlugin.cxx +++ b/src/playlist/plugins/AsxPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,11 +24,12 @@ #include "tag/TagBuilder.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" +#include "util/StringView.hxx" #include "lib/expat/ExpatParser.hxx" #include "Log.hxx" /** - * This is the state object for the GLib XML parser. + * This is the state object for our XML parser. */ struct AsxParser { /** @@ -130,7 +131,8 @@ asx_char_data(void *user_data, const XML_Char *s, int len) case AsxParser::ENTRY: if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) - parser->tag_builder.AddItem(parser->tag_type, s, len); + parser->tag_builder.AddItem(parser->tag_type, + StringView(s, len)); break; } diff --git a/src/playlist/plugins/AsxPlaylistPlugin.hxx b/src/playlist/plugins/AsxPlaylistPlugin.hxx index 63371be0f..b14eeda87 100644 --- a/src/playlist/plugins/AsxPlaylistPlugin.hxx +++ b/src/playlist/plugins/AsxPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/CuePlaylistPlugin.cxx b/src/playlist/plugins/CuePlaylistPlugin.cxx index b907d34d0..df6946abc 100644 --- a/src/playlist/plugins/CuePlaylistPlugin.cxx +++ b/src/playlist/plugins/CuePlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/CuePlaylistPlugin.hxx b/src/playlist/plugins/CuePlaylistPlugin.hxx index 4d833bfc2..6daad4241 100644 --- a/src/playlist/plugins/CuePlaylistPlugin.hxx +++ b/src/playlist/plugins/CuePlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx index 8baa11c03..e12dc2df0 100644 --- a/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx +++ b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -128,8 +128,10 @@ DetachedSong * EmbeddedCuePlaylist::NextSong() { DetachedSong *song = parser->Get(); - if (song != nullptr) + if (song != nullptr) { + song->SetURI(filename); return song; + } while (*next != 0) { const char *line = next; diff --git a/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx index 5eedf3f13..9721481d5 100644 --- a/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx +++ b/src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx index 93316ca6c..e60ef4aa6 100644 --- a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx +++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx index 5743ded43..625afcd2d 100644 --- a/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx +++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/FlacPlaylistPlugin.cxx b/src/playlist/plugins/FlacPlaylistPlugin.cxx new file mode 100644 index 000000000..19b77ef32 --- /dev/null +++ b/src/playlist/plugins/FlacPlaylistPlugin.cxx @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2003-2015 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. + */ + +/** \file + * + * Playlist plugin that reads embedded cue sheets from the "CUESHEET" + * tag of a music file. + */ + +#include "config.h" +#include "FlacPlaylistPlugin.hxx" +#include "../PlaylistPlugin.hxx" +#include "../SongEnumerator.hxx" +#include "DetachedSong.hxx" +#include "fs/Traits.hxx" +#include "fs/AllocatedPath.hxx" +#include "fs/NarrowPath.hxx" + +#include <FLAC/metadata.h> + +#include <string.h> + +class FlacPlaylist final : public SongEnumerator { + const char *const uri; + + FLAC__StreamMetadata *const cuesheet; + const unsigned sample_rate; + const FLAC__uint64 total_samples; + + unsigned next_track = 0; + +public: + FlacPlaylist(const char *_uri, + FLAC__StreamMetadata *_cuesheet, + const FLAC__StreamMetadata &streaminfo) + :uri(_uri), cuesheet(_cuesheet), + sample_rate(streaminfo.data.stream_info.sample_rate), + total_samples(streaminfo.data.stream_info.total_samples) { + } + + virtual ~FlacPlaylist() { + FLAC__metadata_object_delete(cuesheet); + } + + virtual DetachedSong *NextSong() override; +}; + +DetachedSong * +FlacPlaylist::NextSong() +{ + const FLAC__StreamMetadata_CueSheet &c = cuesheet->data.cue_sheet; + + /* find the next audio track */ + + while (next_track < c.num_tracks && + (c.tracks[next_track].number > c.num_tracks || + c.tracks[next_track].type != 0)) + ++next_track; + + if (next_track >= c.num_tracks) + return nullptr; + + FLAC__uint64 start = c.tracks[next_track].offset; + ++next_track; + FLAC__uint64 end = next_track < c.num_tracks + ? c.tracks[next_track].offset + : total_samples; + + auto *song = new DetachedSong(uri); + song->SetStartTime(SongTime::FromScale(start, sample_rate)); + song->SetEndTime(SongTime::FromScale(end, sample_rate)); + return song; +} + +static SongEnumerator * +flac_playlist_open_uri(const char *uri, + gcc_unused Mutex &mutex, gcc_unused Cond &cond) +{ + if (!PathTraitsUTF8::IsAbsolute(uri)) + /* only local files supported */ + return nullptr; + + const auto path_fs = AllocatedPath::FromUTF8(uri); + if (path_fs.IsNull()) + return nullptr; + + const NarrowPath narrow_path_fs(path_fs); + + FLAC__StreamMetadata *cuesheet; + if (!FLAC__metadata_get_cuesheet(narrow_path_fs, &cuesheet)) + return nullptr; + + FLAC__StreamMetadata streaminfo; + if (!FLAC__metadata_get_streaminfo(uri, &streaminfo) || + streaminfo.data.stream_info.sample_rate == 0) { + FLAC__metadata_object_delete(cuesheet); + return nullptr; + } + + return new FlacPlaylist(uri, cuesheet, streaminfo); +} + +static const char *const flac_playlist_suffixes[] = { + "flac", + nullptr +}; + +const struct playlist_plugin flac_playlist_plugin = { + "flac", + + nullptr, + nullptr, + flac_playlist_open_uri, + nullptr, + + nullptr, + flac_playlist_suffixes, + nullptr, +}; diff --git a/src/playlist/plugins/FlacPlaylistPlugin.hxx b/src/playlist/plugins/FlacPlaylistPlugin.hxx new file mode 100644 index 000000000..632be6b12 --- /dev/null +++ b/src/playlist/plugins/FlacPlaylistPlugin.hxx @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2003-2015 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_FLAC_PLAYLIST_PLUGIN_HXX +#define MPD_FLAC_PLAYLIST_PLUGIN_HXX + +extern const struct playlist_plugin flac_playlist_plugin; + +#endif diff --git a/src/playlist/plugins/M3uPlaylistPlugin.cxx b/src/playlist/plugins/M3uPlaylistPlugin.cxx index 0428d291a..9e7647dd7 100644 --- a/src/playlist/plugins/M3uPlaylistPlugin.cxx +++ b/src/playlist/plugins/M3uPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/M3uPlaylistPlugin.hxx b/src/playlist/plugins/M3uPlaylistPlugin.hxx index f1ad14069..9df4482d9 100644 --- a/src/playlist/plugins/M3uPlaylistPlugin.hxx +++ b/src/playlist/plugins/M3uPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/PlsPlaylistPlugin.cxx b/src/playlist/plugins/PlsPlaylistPlugin.cxx index f7724f522..7d2579cd3 100644 --- a/src/playlist/plugins/PlsPlaylistPlugin.cxx +++ b/src/playlist/plugins/PlsPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,121 +21,139 @@ #include "PlsPlaylistPlugin.hxx" #include "../PlaylistPlugin.hxx" #include "../MemorySongEnumerator.hxx" -#include "input/InputStream.hxx" +#include "input/TextInputStream.hxx" #include "DetachedSong.hxx" #include "tag/TagBuilder.hxx" +#include "util/ASCII.hxx" +#include "util/StringUtil.hxx" +#include "util/DivideString.hxx" #include "util/Error.hxx" -#include "util/Domain.hxx" -#include "Log.hxx" - -#include <glib.h> #include <string> -#include <stdio.h> - -#include <stdio.h> -static constexpr Domain pls_domain("pls"); +#include <stdlib.h> -static void -pls_parser(GKeyFile *keyfile, std::forward_list<DetachedSong> &songs) +static bool +FindPlaylistSection(TextInputStream &is) { - gchar *value; - GError *error = nullptr; - int num_entries = g_key_file_get_integer(keyfile, "playlist", - "NumberOfEntries", &error); - if (error) { - FormatError(pls_domain, - "Invalid PLS file: '%s'", error->message); - g_error_free(error); - error = nullptr; - - /* Hack to work around shoutcast failure to comform to spec */ - num_entries = g_key_file_get_integer(keyfile, "playlist", - "numberofentries", &error); - if (error) { - g_error_free(error); - error = nullptr; - } + char *line; + while ((line = is.ReadLine()) != nullptr) { + line = Strip(line); + if (StringEqualsCaseASCII(line, "[playlist]")) + return true; } - for (; num_entries > 0; --num_entries) { - char key[64]; - sprintf(key, "File%u", num_entries); - char *uri = g_key_file_get_string(keyfile, "playlist", key, - &error); - if(error) { - FormatError(pls_domain, "Invalid PLS entry %s: '%s'", - key, error->message); - g_error_free(error); - return; - } + return false; +} - TagBuilder tag; +static bool +ParsePls(TextInputStream &is, std::forward_list<DetachedSong> &songs) +{ + assert(songs.empty()); - sprintf(key, "Title%u", num_entries); - value = g_key_file_get_string(keyfile, "playlist", key, - nullptr); - if (value != nullptr) - tag.AddItem(TAG_TITLE, value); + if (!FindPlaylistSection(is)) + return false; - g_free(value); + unsigned n_entries = 0; - sprintf(key, "Length%u", num_entries); - int length = g_key_file_get_integer(keyfile, "playlist", key, - nullptr); - if (length > 0) - tag.SetDuration(SignedSongTime::FromS(length)); + struct Entry { + std::string file, title; + int length; - songs.emplace_front(uri, tag.Commit()); - g_free(uri); - } + Entry():length(-1) {} + }; -} + static constexpr unsigned MAX_ENTRIES = 65536; -static SongEnumerator * -pls_open_stream(InputStream &is) -{ - GError *error = nullptr; - Error error2; - - std::string kf_data; - - do { - char buffer[1024]; - size_t nbytes = is.LockRead(buffer, sizeof(buffer), error2); - if (nbytes == 0) { - if (error2.IsDefined()) { - LogError(error2); - return nullptr; - } + std::vector<Entry> entries; + + char *line; + while ((line = is.ReadLine()) != nullptr) { + line = Strip(line); + if (*line == 0 || *line == ';') + continue; + + if (*line == '[') + /* another section starts; we only want + [Playlist], so stop here */ break; + + const DivideString ds(line, '=', true); + if (!ds.IsDefined()) + continue; + + const char *const name = ds.GetFirst(); + const char *const value = ds.GetSecond(); + + if (StringEqualsCaseASCII(name, "NumberOfEntries")) { + n_entries = strtoul(value, nullptr, 10); + if (n_entries == 0) + /* empty file - nothing remains to be + done */ + return true; + + if (n_entries > MAX_ENTRIES) + n_entries = MAX_ENTRIES; + entries.resize(n_entries); + } else if (StringEqualsCaseASCII(name, "File", 4)) { + unsigned i = strtoul(name + 4, nullptr, 10); + if (i >= 1 && i <= (n_entries > 0 ? n_entries : MAX_ENTRIES)) { + if (entries.size() < i) + entries.resize(i); + entries[i - 1].file = value; + } + } else if (StringEqualsCaseASCII(name, "Title", 5)) { + unsigned i = strtoul(name + 5, nullptr, 10); + if (i >= 1 && i <= (n_entries > 0 ? n_entries : MAX_ENTRIES)) { + if (entries.size() < i) + entries.resize(i); + entries[i - 1].title = value; + } + } else if (StringEqualsCaseASCII(name, "Length", 6)) { + unsigned i = strtoul(name + 6, nullptr, 10); + if (i >= 1 && i <= (n_entries > 0 ? n_entries : MAX_ENTRIES)) { + if (entries.size() < i) + entries.resize(i); + entries[i - 1].length = atoi(value); + } } + } - kf_data.append(buffer, nbytes); - /* Limit to 64k */ - } while (kf_data.length() < 65536); + if (n_entries == 0) + /* no "NumberOfEntries" found */ + return false; - if (kf_data.empty()) { - LogWarning(pls_domain, "KeyFile parser failed: No Data"); - return nullptr; - } + auto i = songs.before_begin(); + for (const auto &entry : entries) { + const char *uri = entry.file.c_str(); - GKeyFile *keyfile = g_key_file_new(); - if (!g_key_file_load_from_data(keyfile, - kf_data.data(), kf_data.length(), - G_KEY_FILE_NONE, &error)) { - FormatError(pls_domain, - "KeyFile parser failed: %s", error->message); - g_error_free(error); - g_key_file_free(keyfile); - return nullptr; + TagBuilder tag; + if (!entry.title.empty()) + tag.AddItem(TAG_TITLE, entry.title.c_str()); + + if (entry.length > 0) + tag.SetDuration(SignedSongTime::FromS(entry.length)); + + i = songs.emplace_after(i, uri, tag.Commit()); } + return true; +} + +static bool +ParsePls(InputStream &is, std::forward_list<DetachedSong> &songs) +{ + TextInputStream tis(is); + return ParsePls(tis, songs); +} + +static SongEnumerator * +pls_open_stream(InputStream &is) +{ std::forward_list<DetachedSong> songs; - pls_parser(keyfile, songs); - g_key_file_free(keyfile); + if (!ParsePls(is, songs)) + return nullptr; return new MemorySongEnumerator(std::move(songs)); } diff --git a/src/playlist/plugins/PlsPlaylistPlugin.hxx b/src/playlist/plugins/PlsPlaylistPlugin.hxx index 1a3f33873..c7a1e331c 100644 --- a/src/playlist/plugins/PlsPlaylistPlugin.hxx +++ b/src/playlist/plugins/PlsPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/RssPlaylistPlugin.cxx b/src/playlist/plugins/RssPlaylistPlugin.cxx index 6f9aad54b..a2d8e7a42 100644 --- a/src/playlist/plugins/RssPlaylistPlugin.cxx +++ b/src/playlist/plugins/RssPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,11 +24,12 @@ #include "tag/TagBuilder.hxx" #include "util/ASCII.hxx" #include "util/Error.hxx" +#include "util/StringView.hxx" #include "lib/expat/ExpatParser.hxx" #include "Log.hxx" /** - * This is the state object for the GLib XML parser. + * This is the state object for the our XML parser. */ struct RssParser { /** @@ -128,7 +129,8 @@ rss_char_data(void *user_data, const XML_Char *s, int len) case RssParser::ITEM: if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES) - parser->tag_builder.AddItem(parser->tag_type, s, len); + parser->tag_builder.AddItem(parser->tag_type, + StringView(s, len)); break; } diff --git a/src/playlist/plugins/RssPlaylistPlugin.hxx b/src/playlist/plugins/RssPlaylistPlugin.hxx index a00a5a898..4928df904 100644 --- a/src/playlist/plugins/RssPlaylistPlugin.hxx +++ b/src/playlist/plugins/RssPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx index ec4d240a5..d6f25f48c 100644 --- a/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx +++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,20 +21,21 @@ #include "SoundCloudPlaylistPlugin.hxx" #include "../PlaylistPlugin.hxx" #include "../MemorySongEnumerator.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "input/InputStream.hxx" #include "tag/TagBuilder.hxx" #include "util/StringUtil.hxx" +#include "util/Alloc.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "Log.hxx" -#include <glib.h> #include <yajl/yajl_parse.h> #include <string> #include <string.h> +#include <stdlib.h> static struct { std::string apikey; @@ -43,10 +44,10 @@ static struct { static constexpr Domain soundcloud_domain("soundcloud"); static bool -soundcloud_init(const config_param ¶m) +soundcloud_init(const ConfigBlock &block) { // APIKEY for MPD application, registered under DarkFox' account. - soundcloud_config.apikey = param.GetBlockValue("apikey", "a25e51780f7f86af0afa91f241d091f8"); + soundcloud_config.apikey = block.GetBlockValue("apikey", "a25e51780f7f86af0afa91f241d091f8"); if (soundcloud_config.apikey.empty()) { LogDebug(soundcloud_domain, "disabling the soundcloud playlist plugin " @@ -60,7 +61,7 @@ soundcloud_init(const config_param ¶m) /** * Construct a full soundcloud resolver URL from the given fragment. * @param uri uri of a soundcloud page (or just the path) - * @return Constructed URL. Must be freed with g_free. + * @return Constructed URL. Must be freed with free(). */ static char * soundcloud_resolve(const char* uri) @@ -68,18 +69,18 @@ soundcloud_resolve(const char* uri) char *u, *ru; if (StringStartsWith(uri, "https://")) { - u = g_strdup(uri); + u = xstrdup(uri); } else if (StringStartsWith(uri, "soundcloud.com")) { - u = g_strconcat("https://", uri, nullptr); + u = xstrcatdup("https://", uri); } else { /* assume it's just a path on soundcloud.com */ - u = g_strconcat("https://soundcloud.com/", uri, nullptr); + u = xstrcatdup("https://soundcloud.com/", uri); } - ru = g_strconcat("https://api.soundcloud.com/resolve.json?url=", - u, "&client_id=", - soundcloud_config.apikey.c_str(), nullptr); - g_free(u); + ru = xstrcatdup("https://api.soundcloud.com/resolve.json?url=", + u, "&client_id=", + soundcloud_config.apikey.c_str()); + free(u); return ru; } @@ -111,12 +112,7 @@ struct parse_data { }; static int -handle_integer(void *ctx, - long -#ifndef HAVE_YAJL1 - long -#endif - intval) +handle_integer(void *ctx, long long intval) { struct parse_data *data = (struct parse_data *) ctx; @@ -132,25 +128,19 @@ handle_integer(void *ctx, } static int -handle_string(void *ctx, const unsigned char* stringval, -#ifdef HAVE_YAJL1 - unsigned int -#else - size_t -#endif - stringlen) +handle_string(void *ctx, const unsigned char *stringval, size_t stringlen) { struct parse_data *data = (struct parse_data *) ctx; const char *s = (const char *) stringval; switch (data->key) { case Title: - g_free(data->title); - data->title = g_strndup(s, stringlen); + free(data->title); + data->title = xstrndup(s, stringlen); break; case Stream_URL: - g_free(data->stream_url); - data->stream_url = g_strndup(s, stringlen); + free(data->stream_url); + data->stream_url = xstrndup(s, stringlen); data->got_url = 1; break; default: @@ -161,13 +151,7 @@ handle_string(void *ctx, const unsigned char* stringval, } static int -handle_mapkey(void *ctx, const unsigned char* stringval, -#ifdef HAVE_YAJL1 - unsigned int -#else - size_t -#endif - stringlen) +handle_mapkey(void *ctx, const unsigned char *stringval, size_t stringlen) { struct parse_data *data = (struct parse_data *) ctx; @@ -211,8 +195,8 @@ handle_end_map(void *ctx) /* got_url == 1, track finished, make it into a song */ data->got_url = 0; - char *u = g_strconcat(data->stream_url, "?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + char *u = xstrcatdup(data->stream_url, "?client_id=", + soundcloud_config.apikey.c_str()); TagBuilder tag; tag.SetDuration(SignedSongTime::FromMS(data->duration)); @@ -220,7 +204,7 @@ handle_end_map(void *ctx) tag.AddItem(TAG_NAME, data->title); data->songs.emplace_front(u, tag.Commit()); - g_free(u); + free(u); return 1; } @@ -282,20 +266,11 @@ soundcloud_parse_json(const char *url, yajl_handle hand, } if (done) { -#ifdef HAVE_YAJL1 - stat = yajl_parse_complete(hand); -#else stat = yajl_complete_parse(hand); -#endif } else stat = yajl_parse(hand, ubuffer, nbytes); - if (stat != yajl_status_ok -#ifdef HAVE_YAJL1 - && stat != yajl_status_insufficient_data -#endif - ) - { + if (stat != yajl_status_ok) { unsigned char *str = yajl_get_error(hand, 1, ubuffer, nbytes); LogError(soundcloud_domain, (const char *)str); yajl_free_error(hand, str); @@ -325,24 +300,24 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) char *u = nullptr; if (memcmp(uri, "track/", 6) == 0) { const char *rest = uri + 6; - u = g_strconcat("https://api.soundcloud.com/tracks/", - rest, ".json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/tracks/", + rest, ".json?client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "playlist/", 9) == 0) { const char *rest = uri + 9; - u = g_strconcat("https://api.soundcloud.com/playlists/", - rest, ".json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/playlists/", + rest, ".json?client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "user/", 5) == 0) { const char *rest = uri + 5; - u = g_strconcat("https://api.soundcloud.com/users/", - rest, "/tracks.json?client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/users/", + rest, "/tracks.json?client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "search/", 7) == 0) { const char *rest = uri + 7; - u = g_strconcat("https://api.soundcloud.com/tracks.json?q=", - rest, "&client_id=", - soundcloud_config.apikey.c_str(), nullptr); + u = xstrcatdup("https://api.soundcloud.com/tracks.json?q=", + rest, "&client_id=", + soundcloud_config.apikey.c_str()); } else if (memcmp(uri, "url/", 4) == 0) { const char *rest = uri + 4; /* Translate to soundcloud resolver call. libcurl will automatically @@ -359,19 +334,14 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond) data.got_url = 0; data.title = nullptr; data.stream_url = nullptr; -#ifdef HAVE_YAJL1 - yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, nullptr, - &data); -#else yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data); -#endif int ret = soundcloud_parse_json(u, hand, mutex, cond); - g_free(u); + free(u); yajl_free(hand); - g_free(data.title); - g_free(data.stream_url); + free(data.title); + free(data.stream_url); if (ret == -1) return nullptr; diff --git a/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx b/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx index b355b477a..199d21e56 100644 --- a/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx +++ b/src/playlist/plugins/SoundCloudPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/playlist/plugins/XspfPlaylistPlugin.cxx b/src/playlist/plugins/XspfPlaylistPlugin.cxx index d25d6dc28..0b7358e15 100644 --- a/src/playlist/plugins/XspfPlaylistPlugin.cxx +++ b/src/playlist/plugins/XspfPlaylistPlugin.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,13 +25,14 @@ #include "input/InputStream.hxx" #include "tag/TagBuilder.hxx" #include "util/Error.hxx" +#include "util/StringView.hxx" #include "lib/expat/ExpatParser.hxx" #include "Log.hxx" #include <string.h> /** - * This is the state object for the GLib XML parser. + * This is the state object for our XML parser. */ struct XspfParser { /** @@ -170,7 +171,8 @@ xspf_char_data(void *user_data, const XML_Char *s, int len) case XspfParser::TRACK: if (!parser->location.empty() && parser->tag_type != TAG_NUM_OF_ITEM_TYPES) - parser->tag_builder.AddItem(parser->tag_type, s, len); + parser->tag_builder.AddItem(parser->tag_type, + StringView(s, len)); break; diff --git a/src/playlist/plugins/XspfPlaylistPlugin.hxx b/src/playlist/plugins/XspfPlaylistPlugin.hxx index 6b08a6be6..0e95a1445 100644 --- a/src/playlist/plugins/XspfPlaylistPlugin.hxx +++ b/src/playlist/plugins/XspfPlaylistPlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/poison.h b/src/poison.h index c112f6e19..96972cd2e 100644 --- a/src/poison.h +++ b/src/poison.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/protocol/Ack.cxx b/src/protocol/Ack.cxx index 56f0f0b5d..9e0c06614 100644 --- a/src/protocol/Ack.cxx +++ b/src/protocol/Ack.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/protocol/Ack.hxx b/src/protocol/Ack.hxx index e2c4dd9d1..c8457c5b4 100644 --- a/src/protocol/Ack.hxx +++ b/src/protocol/Ack.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/protocol/ArgParser.cxx b/src/protocol/ArgParser.cxx index e373827b4..31756f53e 100644 --- a/src/protocol/ArgParser.cxx +++ b/src/protocol/ArgParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,61 +19,63 @@ #include "config.h" #include "ArgParser.hxx" -#include "Result.hxx" #include "Chrono.hxx" - -#include <limits> +#include "client/Response.hxx" #include <stdlib.h> bool -check_uint32(Client &client, uint32_t *dst, const char *s) +ParseCommandArg32(Response &r, uint32_t &value_r, const char *s) { char *test; - *dst = strtoul(s, &test, 10); + value_r = strtoul(s, &test, 10); if (test == s || *test != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s); return false; } return true; } bool -check_int(Client &client, int *value_r, const char *s) +ParseCommandArg(Response &r, int &value_r, const char *s, + int min_value, int max_value) { char *test; long value; value = strtol(s, &test, 10); if (test == s || *test != '\0') { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s); return false; } - if (value < std::numeric_limits<int>::min() || - value > std::numeric_limits<int>::max()) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); + if (value < min_value || value > max_value) { + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } - *value_r = (int)value; + value_r = (int)value; return true; } bool -check_range(Client &client, unsigned *value_r1, unsigned *value_r2, - const char *s) +ParseCommandArg(Response &r, int &value_r, const char *s) +{ + return ParseCommandArg(r, value_r, s, + std::numeric_limits<int>::min(), + std::numeric_limits<int>::max()); +} + +bool +ParseCommandArg(Response &r, RangeArg &value_r, const char *s) { char *test, *test2; long value; value = strtol(s, &test, 10); if (test == s || (*test != '\0' && *test != ':')) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Integer or range expected: %s", s); return false; } @@ -81,29 +83,27 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2, if (value == -1 && *test == 0) { /* compatibility with older MPD versions: specifying "-1" makes MPD display the whole list */ - *value_r1 = 0; - *value_r2 = std::numeric_limits<int>::max(); + value_r.start = 0; + value_r.end = std::numeric_limits<int>::max(); return true; } if (value < 0) { - command_error(client, ACK_ERROR_ARG, - "Number is negative: %s", s); + r.FormatError(ACK_ERROR_ARG, "Number is negative: %s", s); return false; } if (unsigned(value) > std::numeric_limits<unsigned>::max()) { - command_error(client, ACK_ERROR_ARG, - "Number too large: %s", s); + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } - *value_r1 = (unsigned)value; + value_r.start = (unsigned)value; if (*test == ':') { value = strtol(++test, &test2, 10); if (*test2 != '\0') { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Integer or range expected: %s", s); return false; } @@ -112,87 +112,93 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2, value = std::numeric_limits<int>::max(); if (value < 0) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Number is negative: %s", s); return false; } if (unsigned(value) > std::numeric_limits<unsigned>::max()) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } - *value_r2 = (unsigned)value; + value_r.end = (unsigned)value; } else { - *value_r2 = (unsigned)value + 1; + value_r.end = (unsigned)value + 1; } return true; } bool -check_unsigned(Client &client, unsigned *value_r, const char *s) +ParseCommandArg(Response &r, unsigned &value_r, const char *s, + unsigned max_value) { unsigned long value; char *endptr; value = strtoul(s, &endptr, 10); if (endptr == s || *endptr != 0) { - command_error(client, ACK_ERROR_ARG, - "Integer expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Integer expected: %s", s); return false; } - if (value > std::numeric_limits<unsigned>::max()) { - command_error(client, ACK_ERROR_ARG, + if (value > max_value) { + r.FormatError(ACK_ERROR_ARG, "Number too large: %s", s); return false; } - *value_r = (unsigned)value; + value_r = (unsigned)value; return true; } bool -check_bool(Client &client, bool *value_r, const char *s) +ParseCommandArg(Response &r, unsigned &value_r, const char *s) +{ + return ParseCommandArg(r, value_r, s, + std::numeric_limits<unsigned>::max()); +} + +bool +ParseCommandArg(Response &r, bool &value_r, const char *s) { long value; char *endptr; value = strtol(s, &endptr, 10); if (endptr == s || *endptr != 0 || (value != 0 && value != 1)) { - command_error(client, ACK_ERROR_ARG, + r.FormatError(ACK_ERROR_ARG, "Boolean (0/1) expected: %s", s); return false; } - *value_r = !!value; + value_r = !!value; return true; } bool -check_float(Client &client, float *value_r, const char *s) +ParseCommandArg(Response &r, float &value_r, const char *s) { float value; char *endptr; value = strtof(s, &endptr); if (endptr == s || *endptr != 0) { - command_error(client, ACK_ERROR_ARG, - "Float expected: %s", s); + r.FormatError(ACK_ERROR_ARG, "Float expected: %s", s); return false; } - *value_r = value; + value_r = value; return true; } bool -ParseCommandArg(Client &client, SongTime &value_r, const char *s) +ParseCommandArg(Response &r, SongTime &value_r, const char *s) { float value; - bool success = check_float(client, &value, s) && value >= 0; + bool success = ParseCommandArg(r, value, s) && value >= 0; if (success) value_r = SongTime::FromS(value); @@ -200,10 +206,10 @@ ParseCommandArg(Client &client, SongTime &value_r, const char *s) } bool -ParseCommandArg(Client &client, SignedSongTime &value_r, const char *s) +ParseCommandArg(Response &r, SignedSongTime &value_r, const char *s) { float value; - bool success = check_float(client, &value, s); + bool success = ParseCommandArg(r, value, s); if (success) value_r = SignedSongTime::FromS(value); diff --git a/src/protocol/ArgParser.hxx b/src/protocol/ArgParser.hxx index 0f79e7ab2..f60dbdf50 100644 --- a/src/protocol/ArgParser.hxx +++ b/src/protocol/ArgParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,35 +22,57 @@ #include "check.h" +#include <limits> + #include <stdint.h> -class Client; +class Response; class SongTime; class SignedSongTime; bool -check_uint32(Client &client, uint32_t *dst, const char *s); +ParseCommandArg32(Response &r, uint32_t &value_r, const char *s); + +bool +ParseCommandArg(Response &r, int &value_r, const char *s, + int min_value, int max_value); + +bool +ParseCommandArg(Response &r, int &value_r, const char *s); + +struct RangeArg { + unsigned start, end; + + void SetAll() { + start = 0; + end = std::numeric_limits<unsigned>::max(); + } + + static constexpr RangeArg All() { + return { 0, std::numeric_limits<unsigned>::max() }; + } +}; bool -check_int(Client &client, int *value_r, const char *s); +ParseCommandArg(Response &r, RangeArg &value_r, const char *s); bool -check_range(Client &client, unsigned *value_r1, unsigned *value_r2, - const char *s); +ParseCommandArg(Response &r, unsigned &value_r, const char *s, + unsigned max_value); bool -check_unsigned(Client &client, unsigned *value_r, const char *s); +ParseCommandArg(Response &r, unsigned &value_r, const char *s); bool -check_bool(Client &client, bool *value_r, const char *s); +ParseCommandArg(Response &r, bool &value_r, const char *s); bool -check_float(Client &client, float *value_r, const char *s); +ParseCommandArg(Response &r, float &value_r, const char *s); bool -ParseCommandArg(Client &client, SongTime &value_r, const char *s); +ParseCommandArg(Response &r, SongTime &value_r, const char *s); bool -ParseCommandArg(Client &client, SignedSongTime &value_r, const char *s); +ParseCommandArg(Response &r, SignedSongTime &value_r, const char *s); #endif diff --git a/src/protocol/Result.cxx b/src/protocol/Result.cxx index 3cc5fc33e..6a0da1665 100644 --- a/src/protocol/Result.cxx +++ b/src/protocol/Result.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,36 +21,8 @@ #include "Result.hxx" #include "client/Client.hxx" -#include <assert.h> - -const char *current_command; -int command_list_num; - void command_success(Client &client) { client_puts(client, "OK\n"); } - -void -command_error_v(Client &client, enum ack error, - const char *fmt, va_list args) -{ - assert(current_command != nullptr); - - client_printf(client, "ACK [%i@%i] {%s} ", - (int)error, command_list_num, current_command); - client_vprintf(client, fmt, args); - client_puts(client, "\n"); - - current_command = nullptr; -} - -void -command_error(Client &client, enum ack error, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - command_error_v(client, error, fmt, args); - va_end(args); -} diff --git a/src/protocol/Result.hxx b/src/protocol/Result.hxx index 0ac9d1e6b..d75bbe51b 100644 --- a/src/protocol/Result.hxx +++ b/src/protocol/Result.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,25 +21,10 @@ #define MPD_PROTOCOL_RESULT_HXX #include "check.h" -#include "Compiler.h" -#include "Ack.hxx" - -#include <stdarg.h> class Client; -extern const char *current_command; -extern int command_list_num; - void command_success(Client &client); -void -command_error_v(Client &client, enum ack error, - const char *fmt, va_list args); - -gcc_printf(3,4) -void -command_error(Client &client, enum ack error, const char *fmt, ...); - #endif diff --git a/src/queue/IdTable.hxx b/src/queue/IdTable.hxx index 8e445243d..d1a0008fb 100644 --- a/src/queue/IdTable.hxx +++ b/src/queue/IdTable.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/queue/Playlist.cxx b/src/queue/Playlist.cxx index b2fd673b4..841684272 100644 --- a/src/queue/Playlist.cxx +++ b/src/queue/Playlist.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,7 +20,7 @@ #include "config.h" #include "Playlist.hxx" #include "PlaylistError.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "DetachedSong.hxx" #include "Idle.hxx" #include "Log.hxx" @@ -44,45 +44,52 @@ playlist::TagModified(DetachedSong &&song) idle_add(IDLE_PLAYLIST); } -/** - * Queue a song, addressed by its order number. - */ -static void -playlist_queue_song_order(playlist &playlist, PlayerControl &pc, - unsigned order) +inline void +playlist::QueueSongOrder(PlayerControl &pc, unsigned order) + { - assert(playlist.queue.IsValidOrder(order)); + assert(queue.IsValidOrder(order)); - playlist.queued = order; + queued = order; - const DetachedSong &song = playlist.queue.GetOrder(order); + const DetachedSong &song = queue.GetOrder(order); FormatDebug(playlist_domain, "queue song %i:\"%s\"", - playlist.queued, song.GetURI()); + queued, song.GetURI()); pc.EnqueueSong(new DetachedSong(song)); } -/** - * Called if the player thread has started playing the "queued" song. - */ -static void -playlist_song_started(playlist &playlist, PlayerControl &pc) +void +playlist::SongStarted() +{ + assert(current >= 0); + + /* reset a song's "priority" when playback starts */ + if (queue.SetPriority(queue.OrderToPosition(current), 0, -1, false)) + OnModified(); +} + +inline void +playlist::QueuedSongStarted(PlayerControl &pc) { assert(pc.next_song == nullptr); - assert(playlist.queued >= -1); + assert(queued >= -1); + assert(current >= 0); /* queued song has started: copy queued to current, and notify the clients */ - int current = playlist.current; - playlist.current = playlist.queued; - playlist.queued = -1; + const int old_current = current; + current = queued; + queued = -1; - if(playlist.queue.consume) - playlist.DeleteOrder(pc, current); + if (queue.consume) + DeleteOrder(pc, old_current); idle_add(IDLE_PLAYER); + + SongStarted(); } const DetachedSong * @@ -139,7 +146,7 @@ playlist::UpdateQueuedSong(PlayerControl &pc, const DetachedSong *prev) if (next_order >= 0) { if (next_song != prev) - playlist_queue_song_order(*this, pc, next_order); + QueueSongOrder(pc, next_order); else queued = next_order; } @@ -157,10 +164,9 @@ playlist::PlayOrder(PlayerControl &pc, int order) pc.Play(new DetachedSong(song)); current = order; -} -static void -playlist_resume_playback(playlist &playlist, PlayerControl &pc); + SongStarted(); +} void playlist::SyncWithPlayer(PlayerControl &pc) @@ -180,12 +186,12 @@ playlist::SyncWithPlayer(PlayerControl &pc) should be restarted with the next song. That can happen if the playlist isn't filling the queue fast enough */ - playlist_resume_playback(*this, pc); + ResumePlayback(pc); else { /* check if the player thread has already started playing the queued song */ if (pc_next_song == nullptr && queued != -1) - playlist_song_started(*this, pc); + QueuedSongStarted(pc); pc.Lock(); pc_next_song = pc.next_song; @@ -198,31 +204,27 @@ playlist::SyncWithPlayer(PlayerControl &pc) } } -/** - * The player has stopped for some reason. Check the error, and - * decide whether to re-start playback - */ -static void -playlist_resume_playback(playlist &playlist, PlayerControl &pc) +inline void +playlist::ResumePlayback(PlayerControl &pc) { - assert(playlist.playing); + assert(playing); assert(pc.GetState() == PlayerState::STOP); const auto error = pc.GetErrorType(); if (error == PlayerError::NONE) - playlist.error_count = 0; + error_count = 0; else - ++playlist.error_count; + ++error_count; - if ((playlist.stop_on_error && error != PlayerError::NONE) || + if ((stop_on_error && error != PlayerError::NONE) || error == PlayerError::OUTPUT || - playlist.error_count >= playlist.queue.GetLength()) + error_count >= queue.GetLength()) /* too many errors, or critical error: stop playback */ - playlist.Stop(pc); + Stop(pc); else /* continue playback at the next song */ - playlist.PlayNext(pc); + PlayNext(pc); } void diff --git a/src/queue/Playlist.hxx b/src/queue/Playlist.hxx index ea19d9bba..a185a79c8 100644 --- a/src/queue/Playlist.hxx +++ b/src/queue/Playlist.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -135,6 +135,17 @@ protected: void OnModified(); /** + * Called when playback of a new song starts. Unlike + * QueuedSongStarted(), this also gets called when the user + * manually switches to another song. It may be used for + * playlist fixups. + * + * The song being started is specified by the #current + * attribute. + */ + void SongStarted(); + + /** * Updates the "queued song". Calculates the next song * according to the current one (if MPD isn't playing, it * takes the first song), and queues this song. Clears the @@ -145,6 +156,24 @@ protected: */ void UpdateQueuedSong(PlayerControl &pc, const DetachedSong *prev); + /** + * Queue a song, addressed by its order number. + */ + void QueueSongOrder(PlayerControl &pc, unsigned order); + + /** + * Called when the player thread has started playing the + * "queued" song, i.e. it has switched from one song to the + * next automatically. + */ + void QueuedSongStarted(PlayerControl &pc); + + /** + * The player has stopped for some reason. Check the error, + * and decide whether to re-start playback. + */ + void ResumePlayback(PlayerControl &pc); + public: void BeginBulk(); void CommitBulk(PlayerControl &pc); @@ -266,7 +295,7 @@ public: * Seek within the current song. Fails if MPD is not currently * playing. * - * @param time the time in seconds + * @param seek_time the time * @param relative if true, then the specified time is relative to the * current position */ diff --git a/src/queue/PlaylistControl.cxx b/src/queue/PlaylistControl.cxx index f7e80dc46..f7f0a4225 100644 --- a/src/queue/PlaylistControl.cxx +++ b/src/queue/PlaylistControl.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ #include "config.h" #include "Playlist.hxx" #include "PlaylistError.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "DetachedSong.hxx" #include "Log.hxx" diff --git a/src/queue/PlaylistEdit.cxx b/src/queue/PlaylistEdit.cxx index 22a88dc46..0d15f6a04 100644 --- a/src/queue/PlaylistEdit.cxx +++ b/src/queue/PlaylistEdit.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ #include "config.h" #include "Playlist.hxx" #include "PlaylistError.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "util/UriUtil.hxx" #include "util/Error.hxx" #include "DetachedSong.hxx" diff --git a/src/queue/PlaylistState.cxx b/src/queue/PlaylistState.cxx index 6ea86166e..fa51b1519 100644 --- a/src/queue/PlaylistState.cxx +++ b/src/queue/PlaylistState.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,10 +29,9 @@ #include "queue/QueueSave.hxx" #include "fs/io/TextFile.hxx" #include "fs/io/BufferedOutputStream.hxx" -#include "PlayerControl.hxx" +#include "player/Control.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" -#include "fs/Limits.hxx" #include "util/CharUtil.hxx" #include "util/StringUtil.hxx" #include "Log.hxx" @@ -57,8 +56,6 @@ #define PLAYLIST_STATE_FILE_STATE_PAUSE "pause" #define PLAYLIST_STATE_FILE_STATE_STOP "stop" -#define PLAYLIST_BUFFER_SIZE 2*MPD_PATH_MAX - void playlist_state_save(BufferedOutputStream &os, const struct playlist &playlist, PlayerControl &pc) @@ -195,7 +192,7 @@ playlist_state_restore(const char *line, TextFile &file, current = 0; if (state == PlayerState::PLAY && - config_get_bool(CONF_RESTORE_PAUSED, false)) + config_get_bool(ConfigOption::RESTORE_PAUSED, false)) /* the user doesn't want MPD to auto-start playback after startup; fall back to "pause" */ diff --git a/src/queue/PlaylistState.hxx b/src/queue/PlaylistState.hxx index 3211b1178..9af9ff1f9 100644 --- a/src/queue/PlaylistState.hxx +++ b/src/queue/PlaylistState.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/queue/PlaylistTag.cxx b/src/queue/PlaylistTag.cxx index 556e7f4e9..69b6abae8 100644 --- a/src/queue/PlaylistTag.cxx +++ b/src/queue/PlaylistTag.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/queue/PlaylistUpdate.cxx b/src/queue/PlaylistUpdate.cxx index 8876711ef..9fcd2f911 100644 --- a/src/queue/PlaylistUpdate.cxx +++ b/src/queue/PlaylistUpdate.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/queue/Queue.cxx b/src/queue/Queue.cxx index 99b545ab1..72837e3f4 100644 --- a/src/queue/Queue.cxx +++ b/src/queue/Queue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -402,7 +402,8 @@ Queue::CountSamePriority(unsigned start_order, uint8_t priority) const } bool -Queue::SetPriority(unsigned position, uint8_t priority, int after_order) +Queue::SetPriority(unsigned position, uint8_t priority, int after_order, + bool reorder) { assert(position < length); @@ -414,7 +415,7 @@ Queue::SetPriority(unsigned position, uint8_t priority, int after_order) item->version = version; item->priority = priority; - if (!random) + if (!random || !reorder) /* don't reorder if not in random mode */ return true; diff --git a/src/queue/Queue.hxx b/src/queue/Queue.hxx index 016619e65..770357e3a 100644 --- a/src/queue/Queue.hxx +++ b/src/queue/Queue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -343,7 +343,8 @@ struct Queue { */ void ShuffleRange(unsigned start, unsigned end); - bool SetPriority(unsigned position, uint8_t priority, int after_order); + bool SetPriority(unsigned position, uint8_t priority, int after_order, + bool reorder=true); bool SetPriorityRange(unsigned start_position, unsigned end_position, uint8_t priority, int after_order); diff --git a/src/queue/QueuePrint.cxx b/src/queue/QueuePrint.cxx index 831ecafb9..5ae1a3036 100644 --- a/src/queue/QueuePrint.cxx +++ b/src/queue/QueuePrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ #include "Queue.hxx" #include "SongFilter.hxx" #include "SongPrint.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" /** * Send detailed information about a range of songs in the queue to a @@ -33,70 +33,70 @@ * @param end the index of the last song (excluding) */ static void -queue_print_song_info(Client &client, const Queue &queue, +queue_print_song_info(Response &r, Partition &partition, const Queue &queue, unsigned position) { - song_print_info(client, queue.Get(position)); - client_printf(client, "Pos: %u\nId: %u\n", - position, queue.PositionToId(position)); + song_print_info(r, partition, queue.Get(position)); + r.Format("Pos: %u\nId: %u\n", + position, queue.PositionToId(position)); uint8_t priority = queue.GetPriorityAtPosition(position); if (priority != 0) - client_printf(client, "Prio: %u\n", priority); + r.Format("Prio: %u\n", priority); } void -queue_print_info(Client &client, const Queue &queue, +queue_print_info(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end) { assert(start <= end); assert(end <= queue.GetLength()); for (unsigned i = start; i < end; ++i) - queue_print_song_info(client, queue, i); + queue_print_song_info(r, partition, queue, i); } void -queue_print_uris(Client &client, const Queue &queue, +queue_print_uris(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end) { assert(start <= end); assert(end <= queue.GetLength()); for (unsigned i = start; i < end; ++i) { - client_printf(client, "%i:", i); - song_print_uri(client, queue.Get(i)); + r.Format("%i:", i); + song_print_uri(r, partition, queue.Get(i)); } } void -queue_print_changes_info(Client &client, const Queue &queue, +queue_print_changes_info(Response &r, Partition &partition, const Queue &queue, uint32_t version) { for (unsigned i = 0; i < queue.GetLength(); i++) { if (queue.IsNewerAtPosition(i, version)) - queue_print_song_info(client, queue, i); + queue_print_song_info(r, partition, queue, i); } } void -queue_print_changes_position(Client &client, const Queue &queue, +queue_print_changes_position(Response &r, const Queue &queue, uint32_t version) { for (unsigned i = 0; i < queue.GetLength(); i++) if (queue.IsNewerAtPosition(i, version)) - client_printf(client, "cpos: %i\nId: %i\n", - i, queue.PositionToId(i)); + r.Format("cpos: %i\nId: %i\n", + i, queue.PositionToId(i)); } void -queue_find(Client &client, const Queue &queue, +queue_find(Response &r, Partition &partition, const Queue &queue, const SongFilter &filter) { for (unsigned i = 0; i < queue.GetLength(); i++) { const DetachedSong &song = queue.Get(i); if (filter.Match(song)) - queue_print_song_info(client, queue, i); + queue_print_song_info(r, partition, queue, i); } } diff --git a/src/queue/QueuePrint.hxx b/src/queue/QueuePrint.hxx index 1aa876219..88d28e8ca 100644 --- a/src/queue/QueuePrint.hxx +++ b/src/queue/QueuePrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,27 +28,28 @@ #include <stdint.h> struct Queue; +struct Partition; class SongFilter; -class Client; +class Response; void -queue_print_info(Client &client, const Queue &queue, +queue_print_info(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end); void -queue_print_uris(Client &client, const Queue &queue, +queue_print_uris(Response &r, Partition &partition, const Queue &queue, unsigned start, unsigned end); void -queue_print_changes_info(Client &client, const Queue &queue, +queue_print_changes_info(Response &r, Partition &partition, const Queue &queue, uint32_t version); void -queue_print_changes_position(Client &client, const Queue &queue, +queue_print_changes_position(Response &r, const Queue &queue, uint32_t version); void -queue_find(Client &client, const Queue &queue, +queue_find(Response &response, Partition &partition, const Queue &queue, const SongFilter &filter); #endif diff --git a/src/queue/QueueSave.cxx b/src/queue/QueueSave.cxx index bc2702572..f5c49549e 100644 --- a/src/queue/QueueSave.cxx +++ b/src/queue/QueueSave.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/queue/QueueSave.hxx b/src/queue/QueueSave.hxx index 3fb4dc1a6..3eeacb418 100644 --- a/src/queue/QueueSave.hxx +++ b/src/queue/QueueSave.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/sticker/Match.hxx b/src/sticker/Match.hxx new file mode 100644 index 000000000..4ac2ac383 --- /dev/null +++ b/src/sticker/Match.hxx @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003-2015 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_STICKER_MATCH_HXX +#define MPD_STICKER_MATCH_HXX + +enum class StickerOperator { + /** + * Matches if a sticker with the specified name exists. The + * "value" parameter is ignored (must be nullptr). + */ + EXISTS, + + /** + * Matches if a sticker with the specified name and value + * exists. + */ + EQUALS, + + /** + * Matches if a sticker with the specified name exists with a + * value smaller than the specified one. + */ + LESS_THAN, + + /** + * Matches if a sticker with the specified name exists with a + * value bigger than the specified one. + */ + GREATER_THAN, +}; + +#endif diff --git a/src/sticker/SongSticker.cxx b/src/sticker/SongSticker.cxx index b6f46f167..b4ca6a398 100644 --- a/src/sticker/SongSticker.cxx +++ b/src/sticker/SongSticker.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,46 +23,48 @@ #include "db/LightSong.hxx" #include "db/Interface.hxx" #include "util/Error.hxx" - -#include <glib.h> +#include "util/Alloc.hxx" #include <assert.h> #include <string.h> +#include <stdlib.h> std::string -sticker_song_get_value(const LightSong &song, const char *name) +sticker_song_get_value(const LightSong &song, const char *name, Error &error) { const auto uri = song.GetURI(); - return sticker_load_value("song", uri.c_str(), name); + return sticker_load_value("song", uri.c_str(), name, error); } bool sticker_song_set_value(const LightSong &song, - const char *name, const char *value) + const char *name, const char *value, + Error &error) { const auto uri = song.GetURI(); - return sticker_store_value("song", uri.c_str(), name, value); + return sticker_store_value("song", uri.c_str(), name, value, error); } bool -sticker_song_delete(const LightSong &song) +sticker_song_delete(const LightSong &song, Error &error) { const auto uri = song.GetURI(); - return sticker_delete("song", uri.c_str()); + return sticker_delete("song", uri.c_str(), error); } bool -sticker_song_delete_value(const LightSong &song, const char *name) +sticker_song_delete_value(const LightSong &song, const char *name, + Error &error) { const auto uri = song.GetURI(); - return sticker_delete_value("song", uri.c_str(), name); + return sticker_delete_value("song", uri.c_str(), name, error); } -struct sticker * -sticker_song_get(const LightSong &song) +Sticker * +sticker_song_get(const LightSong &song, Error &error) { const auto uri = song.GetURI(); - return sticker_load("song", uri.c_str()); + return sticker_load("song", uri.c_str(), error); } struct sticker_song_find_data { @@ -95,9 +97,11 @@ sticker_song_find_cb(const char *uri, const char *value, void *user_data) bool sticker_song_find(const Database &db, const char *base_uri, const char *name, + StickerOperator op, const char *value, void (*func)(const LightSong &song, const char *value, void *user_data), - void *user_data) + void *user_data, + Error &error) { struct sticker_song_find_data data; data.db = &db; @@ -109,16 +113,17 @@ sticker_song_find(const Database &db, const char *base_uri, const char *name, if (*data.base_uri != 0) /* append slash to base_uri */ data.base_uri = allocated = - g_strconcat(data.base_uri, "/", nullptr); + xstrcatdup(data.base_uri, "/"); else /* searching in root directory - no trailing slash */ allocated = nullptr; data.base_uri_length = strlen(data.base_uri); - bool success = sticker_find("song", data.base_uri, name, - sticker_song_find_cb, &data); - g_free(allocated); + bool success = sticker_find("song", data.base_uri, name, op, value, + sticker_song_find_cb, &data, + error); + free(allocated); return success; } diff --git a/src/sticker/SongSticker.hxx b/src/sticker/SongSticker.hxx index 5956cd6f9..3eaa1d776 100644 --- a/src/sticker/SongSticker.hxx +++ b/src/sticker/SongSticker.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,13 +20,15 @@ #ifndef MPD_SONG_STICKER_HXX #define MPD_SONG_STICKER_HXX +#include "Match.hxx" #include "Compiler.h" #include <string> struct LightSong; -struct sticker; +struct Sticker; class Database; +class Error; /** * Returns one value from a song's sticker record. The caller must @@ -34,7 +36,7 @@ class Database; */ gcc_pure std::string -sticker_song_get_value(const LightSong &song, const char *name); +sticker_song_get_value(const LightSong &song, const char *name, Error &error); /** * Sets a sticker value in the specified song. Overwrites existing @@ -42,20 +44,22 @@ sticker_song_get_value(const LightSong &song, const char *name); */ bool sticker_song_set_value(const LightSong &song, - const char *name, const char *value); + const char *name, const char *value, + Error &error); /** * Deletes a sticker from the database. All values are deleted. */ bool -sticker_song_delete(const LightSong &song); +sticker_song_delete(const LightSong &song, Error &error); /** * Deletes a sticker value. Does nothing if the sticker did not * exist. */ bool -sticker_song_delete_value(const LightSong &song, const char *name); +sticker_song_delete_value(const LightSong &song, const char *name, + Error &error); /** * Loads the sticker for the specified song. @@ -63,8 +67,8 @@ sticker_song_delete_value(const LightSong &song, const char *name); * @param song the song object * @return a sticker object, or NULL on error or if there is no sticker */ -sticker * -sticker_song_get(const LightSong &song); +Sticker * +sticker_song_get(const LightSong &song, Error &error); /** * Finds stickers with the specified name below the specified @@ -79,8 +83,10 @@ sticker_song_get(const LightSong &song); */ bool sticker_song_find(const Database &db, const char *base_uri, const char *name, + StickerOperator op, const char *value, void (*func)(const LightSong &song, const char *value, void *user_data), - void *user_data); + void *user_data, + Error &error); #endif diff --git a/src/sticker/StickerDatabase.cxx b/src/sticker/StickerDatabase.cxx index 93eaa900d..02eed362e 100644 --- a/src/sticker/StickerDatabase.cxx +++ b/src/sticker/StickerDatabase.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,24 +19,19 @@ #include "config.h" #include "StickerDatabase.hxx" +#include "lib/sqlite/Domain.hxx" +#include "lib/sqlite/Util.hxx" #include "fs/Path.hxx" #include "Idle.hxx" #include "util/Error.hxx" -#include "util/Domain.hxx" #include "util/Macros.hxx" -#include "Log.hxx" #include <string> #include <map> -#include <sqlite3.h> #include <assert.h> -#if SQLITE_VERSION_NUMBER < 3003009 -#define sqlite3_prepare_v2 sqlite3_prepare -#endif - -struct sticker { +struct Sticker { std::map<std::string, std::string> table; }; @@ -48,6 +43,9 @@ enum sticker_sql { STICKER_SQL_DELETE, STICKER_SQL_DELETE_VALUE, STICKER_SQL_FIND, + STICKER_SQL_FIND_VALUE, + STICKER_SQL_FIND_LT, + STICKER_SQL_FIND_GT, }; static const char *const sticker_sql[] = { @@ -65,6 +63,15 @@ static const char *const sticker_sql[] = { "DELETE FROM sticker WHERE type=? AND uri=? AND name=?", //[STICKER_SQL_FIND] = "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=?", + + //[STICKER_SQL_FIND_VALUE] = + "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value=?", + + //[STICKER_SQL_FIND_LT] = + "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value<?", + + //[STICKER_SQL_FIND_GT] = + "SELECT uri,value FROM sticker WHERE type=? AND uri LIKE (? || '%') AND name=? AND value>?", }; static const char sticker_sql_create[] = @@ -81,23 +88,13 @@ static const char sticker_sql_create[] = static sqlite3 *sticker_db; static sqlite3_stmt *sticker_stmt[ARRAY_SIZE(sticker_sql)]; -static constexpr Domain sticker_domain("sticker"); - -static void -LogError(sqlite3 *db, const char *msg) -{ - FormatError(sticker_domain, "%s: %s", msg, sqlite3_errmsg(db)); -} - static sqlite3_stmt * sticker_prepare(const char *sql, Error &error) { - int ret; sqlite3_stmt *stmt; - - ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, nullptr); + int ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, nullptr); if (ret != SQLITE_OK) { - error.Format(sticker_domain, ret, + error.Format(sqlite_domain, ret, "sqlite3_prepare_v2() failed: %s", sqlite3_errmsg(sticker_db)); return nullptr; @@ -118,9 +115,9 @@ sticker_global_init(Path path, Error &error) ret = sqlite3_open(path.c_str(), &sticker_db); if (ret != SQLITE_OK) { const std::string utf8 = path.ToUTF8(); - error.Format(sticker_domain, ret, - "Failed to open sqlite database '%s': %s", - utf8.c_str(), sqlite3_errmsg(sticker_db)); + error.Format(sqlite_domain, ret, + "Failed to open sqlite database '%s': %s", + utf8.c_str(), sqlite3_errmsg(sticker_db)); return false; } @@ -129,7 +126,7 @@ sticker_global_init(Path path, Error &error) ret = sqlite3_exec(sticker_db, sticker_sql_create, nullptr, nullptr, nullptr); if (ret != SQLITE_OK) { - error.Format(sticker_domain, ret, + error.Format(sqlite_domain, ret, "Failed to create sticker table: %s", sqlite3_errmsg(sticker_db)); return false; @@ -149,7 +146,7 @@ sticker_global_init(Path path, Error &error) } void -sticker_global_finish(void) +sticker_global_finish() { if (sticker_db == nullptr) /* not configured */ @@ -165,16 +162,16 @@ sticker_global_finish(void) } bool -sticker_enabled(void) +sticker_enabled() { return sticker_db != nullptr; } std::string -sticker_load_value(const char *type, const char *uri, const char *name) +sticker_load_value(const char *type, const char *uri, const char *name, + Error &error) { sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_GET]; - int ret; assert(sticker_enabled()); assert(type != nullptr); @@ -184,40 +181,12 @@ sticker_load_value(const char *type, const char *uri, const char *name) if (*name == 0) return std::string(); - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return std::string(); - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return std::string(); - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + if (!BindAll(error, stmt, type, uri, name)) return std::string(); - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); std::string value; - if (ret == SQLITE_ROW) { - /* record found */ + if (ExecuteRow(stmt, error)) value = (const char*)sqlite3_column_text(stmt, 0); - } else if (ret == SQLITE_DONE) { - /* no record found */ - } else { - /* error */ - LogError(sticker_db, "sqlite3_step() failed"); - } sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); @@ -227,63 +196,36 @@ sticker_load_value(const char *type, const char *uri, const char *name) static bool sticker_list_values(std::map<std::string, std::string> &table, - const char *type, const char *uri) + const char *type, const char *uri, + Error &error) { sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_LIST]; - int ret; assert(type != nullptr); assert(uri != nullptr); assert(sticker_enabled()); - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + if (!BindAll(error, stmt, type, uri)) return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - do { - ret = sqlite3_step(stmt); - switch (ret) { - const char *name, *value; - - case SQLITE_ROW: - name = (const char*)sqlite3_column_text(stmt, 0); - value = (const char*)sqlite3_column_text(stmt, 1); + const bool success = ExecuteForEach(stmt, error, [stmt, &table](){ + const char *name = (const char *)sqlite3_column_text(stmt, 0); + const char *value = (const char *)sqlite3_column_text(stmt, 1); table.insert(std::make_pair(name, value)); - break; - case SQLITE_DONE: - break; - case SQLITE_BUSY: - /* no op */ - break; - default: - LogError(sticker_db, "sqlite3_step() failed"); - return false; - } - } while (ret != SQLITE_DONE); + }); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); - return true; + return success; } static bool sticker_update_value(const char *type, const char *uri, - const char *name, const char *value) + const char *name, const char *value, + Error &error) { sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_UPDATE]; - int ret; assert(type != nullptr); assert(uri != nullptr); @@ -293,56 +235,25 @@ sticker_update_value(const char *type, const char *uri, assert(sticker_enabled()); - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, value, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + if (!BindAll(error, stmt, value, type, uri, name)) return false; - } - ret = sqlite3_bind_text(stmt, 3, uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 4, name, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - LogError(sticker_db, "sqlite3_step() failed"); - return false; - } - - ret = sqlite3_changes(sticker_db); + bool modified = ExecuteModified(stmt, error); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); - idle_add(IDLE_STICKER); - return ret > 0; + if (modified) + idle_add(IDLE_STICKER); + return modified; } static bool sticker_insert_value(const char *type, const char *uri, - const char *name, const char *value) + const char *name, const char *value, + Error &error) { sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_INSERT]; - int ret; assert(type != nullptr); assert(uri != nullptr); @@ -352,52 +263,23 @@ sticker_insert_value(const char *type, const char *uri, assert(sticker_enabled()); - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 4, value, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + if (!BindAll(error, stmt, type, uri, name, value)) return false; - } - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - LogError(sticker_db, "sqlite3_step() failed"); - return false; - } + bool success = ExecuteCommand(stmt, error); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); - - idle_add(IDLE_STICKER); - return true; + if (success) + idle_add(IDLE_STICKER); + return success; } bool sticker_store_value(const char *type, const char *uri, - const char *name, const char *value) + const char *name, const char *value, + Error &error) { assert(sticker_enabled()); assert(type != nullptr); @@ -408,106 +290,63 @@ sticker_store_value(const char *type, const char *uri, if (*name == 0) return false; - return sticker_update_value(type, uri, name, value) || - sticker_insert_value(type, uri, name, value); + return sticker_update_value(type, uri, name, value, error) || + sticker_insert_value(type, uri, name, value, error); } bool -sticker_delete(const char *type, const char *uri) +sticker_delete(const char *type, const char *uri, Error &error) { sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE]; - int ret; assert(sticker_enabled()); assert(type != nullptr); assert(uri != nullptr); - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + if (!BindAll(error, stmt, type, uri)) return false; - } - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - LogError(sticker_db, "sqlite3_step() failed"); - return false; - } + bool modified = ExecuteModified(stmt, error); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); - idle_add(IDLE_STICKER); - return true; + if (modified) + idle_add(IDLE_STICKER); + return modified; } bool -sticker_delete_value(const char *type, const char *uri, const char *name) +sticker_delete_value(const char *type, const char *uri, const char *name, + Error &error) { sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_DELETE_VALUE]; - int ret; assert(sticker_enabled()); assert(type != nullptr); assert(uri != nullptr); - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - - ret = sqlite3_bind_text(stmt, 2, uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + if (!BindAll(error, stmt, type, uri, name)) return false; - } - - ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } - do { - ret = sqlite3_step(stmt); - } while (ret == SQLITE_BUSY); - - if (ret != SQLITE_DONE) { - LogError(sticker_db, "sqlite3_step() failed"); - return false; - } - - ret = sqlite3_changes(sticker_db); + bool modified = ExecuteModified(stmt, error); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); - idle_add(IDLE_STICKER); - return ret > 0; + if (modified) + idle_add(IDLE_STICKER); + return modified; } void -sticker_free(struct sticker *sticker) +sticker_free(Sticker *sticker) { delete sticker; } const char * -sticker_get_value(const struct sticker &sticker, const char *name) +sticker_get_value(const Sticker &sticker, const char *name) { auto i = sticker.table.find(name); if (i == sticker.table.end()) @@ -517,7 +356,7 @@ sticker_get_value(const struct sticker &sticker, const char *name) } void -sticker_foreach(const sticker &sticker, +sticker_foreach(const Sticker &sticker, void (*func)(const char *name, const char *value, void *user_data), void *user_data) @@ -526,79 +365,82 @@ sticker_foreach(const sticker &sticker, func(i.first.c_str(), i.second.c_str(), user_data); } -struct sticker * -sticker_load(const char *type, const char *uri) +Sticker * +sticker_load(const char *type, const char *uri, Error &error) { - sticker s; + Sticker s; - if (!sticker_list_values(s.table, type, uri)) + if (!sticker_list_values(s.table, type, uri, error)) return nullptr; if (s.table.empty()) /* don't return empty sticker objects */ return nullptr; - return new sticker(std::move(s)); + return new Sticker(std::move(s)); } -bool -sticker_find(const char *type, const char *base_uri, const char *name, - void (*func)(const char *uri, const char *value, - void *user_data), - void *user_data) +static sqlite3_stmt * +BindFind(const char *type, const char *base_uri, const char *name, + StickerOperator op, const char *value, + Error &error) { - sqlite3_stmt *const stmt = sticker_stmt[STICKER_SQL_FIND]; - int ret; - assert(type != nullptr); assert(name != nullptr); - assert(func != nullptr); - assert(sticker_enabled()); - - sqlite3_reset(stmt); - - ret = sqlite3_bind_text(stmt, 1, type, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; - } if (base_uri == nullptr) base_uri = ""; - ret = sqlite3_bind_text(stmt, 2, base_uri, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); - return false; + switch (op) { + case StickerOperator::EXISTS: + return BindAllOrNull(error, sticker_stmt[STICKER_SQL_FIND], + type, base_uri, name); + + case StickerOperator::EQUALS: + return BindAllOrNull(error, + sticker_stmt[STICKER_SQL_FIND_VALUE], + type, base_uri, name, value); + + case StickerOperator::LESS_THAN: + return BindAllOrNull(error, + sticker_stmt[STICKER_SQL_FIND_LT], + type, base_uri, name, value); + + case StickerOperator::GREATER_THAN: + return BindAllOrNull(error, + sticker_stmt[STICKER_SQL_FIND_GT], + type, base_uri, name, value); } - ret = sqlite3_bind_text(stmt, 3, name, -1, nullptr); - if (ret != SQLITE_OK) { - LogError(sticker_db, "sqlite3_bind_text() failed"); + assert(false); + gcc_unreachable(); +} + +bool +sticker_find(const char *type, const char *base_uri, const char *name, + StickerOperator op, const char *value, + void (*func)(const char *uri, const char *value, + void *user_data), + void *user_data, + Error &error) +{ + assert(func != nullptr); + assert(sticker_enabled()); + + sqlite3_stmt *const stmt = BindFind(type, base_uri, name, op, value, + error); + if (stmt == nullptr) return false; - } - do { - ret = sqlite3_step(stmt); - switch (ret) { - case SQLITE_ROW: + const bool success = ExecuteForEach(stmt, error, + [stmt, func, user_data](){ func((const char*)sqlite3_column_text(stmt, 0), (const char*)sqlite3_column_text(stmt, 1), user_data); - break; - case SQLITE_DONE: - break; - case SQLITE_BUSY: - /* no op */ - break; - default: - LogError(sticker_db, "sqlite3_step() failed"); - return false; - } - } while (ret != SQLITE_DONE); + }); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); - return true; + return success; } diff --git a/src/sticker/StickerDatabase.hxx b/src/sticker/StickerDatabase.hxx index 8993489c4..d9a5ecf11 100644 --- a/src/sticker/StickerDatabase.hxx +++ b/src/sticker/StickerDatabase.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -42,13 +42,14 @@ #ifndef MPD_STICKER_DATABASE_HXX #define MPD_STICKER_DATABASE_HXX +#include "Match.hxx" #include "Compiler.h" #include <string> class Error; class Path; -struct sticker; +struct Sticker; /** * Opens the sticker database. @@ -62,21 +63,22 @@ sticker_global_init(Path path, Error &error); * Close the sticker database. */ void -sticker_global_finish(void); +sticker_global_finish(); /** * Returns true if the sticker database is configured and available. */ gcc_const bool -sticker_enabled(void); +sticker_enabled(); /** * Returns one value from an object's sticker record. Returns an * empty string if the value doesn't exist. */ std::string -sticker_load_value(const char *type, const char *uri, const char *name); +sticker_load_value(const char *type, const char *uri, const char *name, + Error &error); /** * Sets a sticker value in the specified object. Overwrites existing @@ -84,21 +86,24 @@ sticker_load_value(const char *type, const char *uri, const char *name); */ bool sticker_store_value(const char *type, const char *uri, - const char *name, const char *value); + const char *name, const char *value, + Error &error); /** * Deletes a sticker from the database. All sticker values of the * specified object are deleted. */ bool -sticker_delete(const char *type, const char *uri); +sticker_delete(const char *type, const char *uri, + Error &error); /** * Deletes a sticker value. Fails if no sticker with this name * exists. */ bool -sticker_delete_value(const char *type, const char *uri, const char *name); +sticker_delete_value(const char *type, const char *uri, const char *name, + Error &error); /** * Frees resources held by the sticker object. @@ -106,7 +111,7 @@ sticker_delete_value(const char *type, const char *uri, const char *name); * @param sticker the sticker object to be freed */ void -sticker_free(sticker *sticker); +sticker_free(Sticker *sticker); /** * Determines a single value in a sticker. @@ -117,7 +122,7 @@ sticker_free(sticker *sticker); */ gcc_pure const char * -sticker_get_value(const sticker &sticker, const char *name); +sticker_get_value(const Sticker &sticker, const char *name); /** * Iterates over all sticker items in a sticker. @@ -127,7 +132,7 @@ sticker_get_value(const sticker &sticker, const char *name); * @param user_data an opaque pointer for the callback function */ void -sticker_foreach(const sticker &sticker, +sticker_foreach(const Sticker &sticker, void (*func)(const char *name, const char *value, void *user_data), void *user_data); @@ -139,8 +144,9 @@ sticker_foreach(const sticker &sticker, * @param uri the URI of the resource, e.g. the song path * @return a sticker object, or nullptr on error or if there is no sticker */ -sticker * -sticker_load(const char *type, const char *uri); +Sticker * +sticker_load(const char *type, const char *uri, + Error &error); /** * Finds stickers with the specified name below the specified URI. @@ -149,13 +155,17 @@ sticker_load(const char *type, const char *uri); * @param base_uri the URI prefix of the resources, or nullptr if all * resources should be searched * @param name the name of the sticker + * @param op the comparison operator + * @param value the operand * @return true on success (even if no sticker was found), false on * failure */ bool sticker_find(const char *type, const char *base_uri, const char *name, + StickerOperator op, const char *value, void (*func)(const char *uri, const char *value, void *user_data), - void *user_data); + void *user_data, + Error &error); #endif diff --git a/src/sticker/StickerPrint.cxx b/src/sticker/StickerPrint.cxx index a952ff203..f0043ebc8 100644 --- a/src/sticker/StickerPrint.cxx +++ b/src/sticker/StickerPrint.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,25 +20,25 @@ #include "config.h" #include "StickerPrint.hxx" #include "StickerDatabase.hxx" -#include "client/Client.hxx" +#include "client/Response.hxx" void -sticker_print_value(Client &client, +sticker_print_value(Response &r, const char *name, const char *value) { - client_printf(client, "sticker: %s=%s\n", name, value); + r.Format("sticker: %s=%s\n", name, value); } static void print_sticker_cb(const char *name, const char *value, void *data) { - Client &client = *(Client *)data; + auto &r = *(Response *)data; - sticker_print_value(client, name, value); + sticker_print_value(r, name, value); } void -sticker_print(Client &client, const sticker &sticker) +sticker_print(Response &r, const Sticker &sticker) { - sticker_foreach(sticker, print_sticker_cb, &client); + sticker_foreach(sticker, print_sticker_cb, &r); } diff --git a/src/sticker/StickerPrint.hxx b/src/sticker/StickerPrint.hxx index 39f3dc09e..e431245e4 100644 --- a/src/sticker/StickerPrint.hxx +++ b/src/sticker/StickerPrint.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,19 +20,19 @@ #ifndef MPD_STICKER_PRINT_HXX #define MPD_STICKER_PRINT_HXX -struct sticker; -class Client; +struct Sticker; +class Response; /** * Sends one sticker value to the client. */ void -sticker_print_value(Client &client, const char *name, const char *value); +sticker_print_value(Response &r, const char *name, const char *value); /** * Sends all sticker values to the client. */ void -sticker_print(Client &client, const sticker &sticker); +sticker_print(Response &r, const Sticker &sticker); #endif diff --git a/src/storage/CompositeStorage.cxx b/src/storage/CompositeStorage.cxx index 89a2fc756..10a478c0d 100644 --- a/src/storage/CompositeStorage.cxx +++ b/src/storage/CompositeStorage.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -56,7 +56,7 @@ public: /* virtual methods from class StorageDirectoryReader */ const char *Read() override; - bool GetInfo(bool follow, FileInfo &info, Error &error) override; + bool GetInfo(bool follow, StorageFileInfo &info, Error &error) override; }; const char * @@ -81,7 +81,7 @@ CompositeDirectoryReader::Read() } bool -CompositeDirectoryReader::GetInfo(bool follow, FileInfo &info, +CompositeDirectoryReader::GetInfo(bool follow, StorageFileInfo &info, Error &error) { if (other != nullptr) @@ -89,7 +89,7 @@ CompositeDirectoryReader::GetInfo(bool follow, FileInfo &info, assert(current != names.end()); - info.type = FileInfo::Type::DIRECTORY; + info.type = StorageFileInfo::Type::DIRECTORY; info.mtime = 0; info.device = 0; info.inode = 0; @@ -137,7 +137,7 @@ CompositeStorage::Directory::Make(const char *uri) Directory *directory = this; while (*uri != 0) { const std::string name = NextSegment(uri); -#if defined(__clang__) || GCC_CHECK_VERSION(4,8) +#if CLANG_OR_GCC_VERSION(4,8) auto i = directory->children.emplace(std::move(name), Directory()); #else @@ -275,7 +275,7 @@ CompositeStorage::FindStorage(const char *uri, Error &error) const } bool -CompositeStorage::GetInfo(const char *uri, bool follow, FileInfo &info, +CompositeStorage::GetInfo(const char *uri, bool follow, StorageFileInfo &info, Error &error) { const ScopeLock protect(mutex); @@ -288,7 +288,7 @@ CompositeStorage::GetInfo(const char *uri, bool follow, FileInfo &info, const Directory *directory = f.directory->Find(f.uri); if (directory != nullptr) { error.Clear(); - info.type = FileInfo::Type::DIRECTORY; + info.type = StorageFileInfo::Type::DIRECTORY; info.mtime = 0; info.device = 0; info.inode = 0; diff --git a/src/storage/CompositeStorage.hxx b/src/storage/CompositeStorage.hxx index c3695c79d..08717edef 100644 --- a/src/storage/CompositeStorage.hxx +++ b/src/storage/CompositeStorage.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -122,7 +122,7 @@ public: bool Unmount(const char *uri); /* virtual methods from class Storage */ - bool GetInfo(const char *uri, bool follow, FileInfo &info, + bool GetInfo(const char *uri, bool follow, StorageFileInfo &info, Error &error) override; StorageDirectoryReader *OpenDirectory(const char *uri, diff --git a/src/storage/Configured.cxx b/src/storage/Configured.cxx index 41541673b..d3a55eab8 100644 --- a/src/storage/Configured.cxx +++ b/src/storage/Configured.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -44,7 +44,7 @@ CreateConfiguredStorageUri(EventLoop &event_loop, const char *uri, static AllocatedPath GetConfiguredMusicDirectory(Error &error) { - AllocatedPath path = config_get_path(CONF_MUSIC_DIR, error); + AllocatedPath path = config_get_path(ConfigOption::MUSIC_DIR, error); if (path.IsNull() && !error.IsDefined()) path = GetUserMusicDir(); @@ -68,7 +68,7 @@ CreateConfiguredStorage(EventLoop &event_loop, Error &error) { assert(!error.IsDefined()); - auto uri = config_get_string(CONF_MUSIC_DIR, nullptr); + auto uri = config_get_string(ConfigOption::MUSIC_DIR); if (uri != nullptr && uri_has_scheme(uri)) return CreateConfiguredStorageUri(event_loop, uri, error); @@ -78,5 +78,5 @@ CreateConfiguredStorage(EventLoop &event_loop, Error &error) bool IsStorageConfigured() { - return config_get_string(CONF_MUSIC_DIR, nullptr) != nullptr; + return config_get_string(ConfigOption::MUSIC_DIR) != nullptr; } diff --git a/src/storage/Configured.hxx b/src/storage/Configured.hxx index 828a192c3..6769da5ff 100644 --- a/src/storage/Configured.hxx +++ b/src/storage/Configured.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/FileInfo.hxx b/src/storage/FileInfo.hxx index 8dd152c0a..4ba842811 100644 --- a/src/storage/FileInfo.hxx +++ b/src/storage/FileInfo.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ #include <time.h> #include <stdint.h> -struct FileInfo { +struct StorageFileInfo { enum class Type : uint8_t { OTHER, REGULAR, diff --git a/src/storage/MemoryDirectoryReader.cxx b/src/storage/MemoryDirectoryReader.cxx index 160836b1a..e6875435c 100644 --- a/src/storage/MemoryDirectoryReader.cxx +++ b/src/storage/MemoryDirectoryReader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -37,7 +37,8 @@ MemoryStorageDirectoryReader::Read() } bool -MemoryStorageDirectoryReader::GetInfo(gcc_unused bool follow, FileInfo &info, +MemoryStorageDirectoryReader::GetInfo(gcc_unused bool follow, + StorageFileInfo &info, gcc_unused Error &error) { assert(!first); diff --git a/src/storage/MemoryDirectoryReader.hxx b/src/storage/MemoryDirectoryReader.hxx index 1345082cb..69299d1d4 100644 --- a/src/storage/MemoryDirectoryReader.hxx +++ b/src/storage/MemoryDirectoryReader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ public: struct Entry { std::string name; - FileInfo info; + StorageFileInfo info; template<typename N> explicit Entry(N &&_name):name(std::forward<N>(_name)) {} @@ -61,7 +61,8 @@ public: /* virtual methods from class StorageDirectoryReader */ const char *Read() override; - bool GetInfo(bool follow, FileInfo &info, Error &error) override; + bool GetInfo(bool follow, StorageFileInfo &info, + Error &error) override; }; #endif diff --git a/src/storage/Registry.cxx b/src/storage/Registry.cxx index d8e273fd5..a59ec01aa 100644 --- a/src/storage/Registry.cxx +++ b/src/storage/Registry.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/Registry.hxx b/src/storage/Registry.hxx index cb3a78f11..8d7828865 100644 --- a/src/storage/Registry.hxx +++ b/src/storage/Registry.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/StorageInterface.cxx b/src/storage/StorageInterface.cxx index 93c50a8ac..79f0815d7 100644 --- a/src/storage/StorageInterface.cxx +++ b/src/storage/StorageInterface.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/StorageInterface.hxx b/src/storage/StorageInterface.hxx index 4484815bc..4435bbf91 100644 --- a/src/storage/StorageInterface.hxx +++ b/src/storage/StorageInterface.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,7 +25,7 @@ #include <string> -struct FileInfo; +struct StorageFileInfo; class AllocatedPath; class Error; @@ -36,7 +36,8 @@ public: virtual ~StorageDirectoryReader() {} virtual const char *Read() = 0; - virtual bool GetInfo(bool follow, FileInfo &info, Error &error) = 0; + virtual bool GetInfo(bool follow, StorageFileInfo &info, + Error &error) = 0; }; class Storage { @@ -45,7 +46,8 @@ public: Storage(const Storage &) = delete; virtual ~Storage() {} - virtual bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + virtual bool GetInfo(const char *uri_utf8, bool follow, + StorageFileInfo &info, Error &error) = 0; virtual StorageDirectoryReader *OpenDirectory(const char *uri_utf8, diff --git a/src/storage/StoragePlugin.hxx b/src/storage/StoragePlugin.hxx index 15f431105..962963d2f 100644 --- a/src/storage/StoragePlugin.hxx +++ b/src/storage/StoragePlugin.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/plugins/LocalStorage.cxx b/src/storage/plugins/LocalStorage.cxx index b965ceea8..83903ec81 100644 --- a/src/storage/plugins/LocalStorage.cxx +++ b/src/storage/plugins/LocalStorage.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,7 +23,7 @@ #include "storage/StorageInterface.hxx" #include "storage/FileInfo.hxx" #include "util/Error.hxx" -#include "fs/FileSystem.hxx" +#include "fs/FileInfo.hxx" #include "fs/AllocatedPath.hxx" #include "fs/DirectoryReader.hxx" @@ -46,7 +46,8 @@ public: /* virtual methods from class StorageDirectoryReader */ const char *Read() override; - bool GetInfo(bool follow, FileInfo &info, Error &error) override; + bool GetInfo(bool follow, StorageFileInfo &info, + Error &error) override; }; class LocalStorage final : public Storage { @@ -61,7 +62,7 @@ public: } /* virtual methods from class Storage */ - bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + bool GetInfo(const char *uri_utf8, bool follow, StorageFileInfo &info, Error &error) override; StorageDirectoryReader *OpenDirectory(const char *uri_utf8, @@ -78,28 +79,27 @@ private: }; static bool -Stat(Path path, bool follow, FileInfo &info, Error &error) +Stat(Path path, bool follow, StorageFileInfo &info, Error &error) { - struct stat st; - if (!StatFile(path, st, follow)) { - error.SetErrno(); - - const auto path_utf8 = path.ToUTF8(); - error.FormatPrefix("Failed to stat %s: ", path_utf8.c_str()); + FileInfo src; + if (!GetFileInfo(path, src, follow, error)) return false; - } - if (S_ISREG(st.st_mode)) - info.type = FileInfo::Type::REGULAR; - else if (S_ISDIR(st.st_mode)) - info.type = FileInfo::Type::DIRECTORY; + if (src.IsRegular()) + info.type = StorageFileInfo::Type::REGULAR; + else if (src.IsDirectory()) + info.type = StorageFileInfo::Type::DIRECTORY; else - info.type = FileInfo::Type::OTHER; - - info.size = st.st_size; - info.mtime = st.st_mtime; - info.device = st.st_dev; - info.inode = st.st_ino; + info.type = StorageFileInfo::Type::OTHER; + + info.size = src.GetSize(); + info.mtime = src.GetModificationTime(); +#ifdef WIN32 + info.device = info.inode = 0; +#else + info.device = src.GetDevice(); + info.inode = src.GetInode(); +#endif return true; } @@ -142,7 +142,7 @@ LocalStorage::MapToRelativeUTF8(const char *uri_utf8) const } bool -LocalStorage::GetInfo(const char *uri_utf8, bool follow, FileInfo &info, +LocalStorage::GetInfo(const char *uri_utf8, bool follow, StorageFileInfo &info, Error &error) { AllocatedPath path_fs = MapFS(uri_utf8, error); @@ -172,7 +172,7 @@ LocalStorage::OpenDirectory(const char *uri_utf8, Error &error) gcc_pure static bool -SkipNameFS(const char *name_fs) +SkipNameFS(PathTraitsFS::const_pointer name_fs) { return name_fs[0] == '.' && (name_fs[1] == 0 || @@ -198,7 +198,7 @@ LocalDirectoryReader::Read() } bool -LocalDirectoryReader::GetInfo(bool follow, FileInfo &info, Error &error) +LocalDirectoryReader::GetInfo(bool follow, StorageFileInfo &info, Error &error) { const AllocatedPath path_fs = AllocatedPath::Build(base_fs, reader.GetEntry()); diff --git a/src/storage/plugins/LocalStorage.hxx b/src/storage/plugins/LocalStorage.hxx index 7295d38e7..ea6bc357c 100644 --- a/src/storage/plugins/LocalStorage.hxx +++ b/src/storage/plugins/LocalStorage.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/plugins/NfsStorage.cxx b/src/storage/plugins/NfsStorage.cxx index a1f079e2c..fc4fd5c07 100644 --- a/src/storage/plugins/NfsStorage.cxx +++ b/src/storage/plugins/NfsStorage.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -84,7 +84,7 @@ public: } /* virtual methods from class Storage */ - bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + bool GetInfo(const char *uri_utf8, bool follow, StorageFileInfo &info, Error &error) override; StorageDirectoryReader *OpenDirectory(const char *uri_utf8, @@ -245,14 +245,14 @@ NfsStorage::MapToRelativeUTF8(const char *uri_utf8) const } static void -Copy(FileInfo &info, const struct stat &st) +Copy(StorageFileInfo &info, const struct stat &st) { if (S_ISREG(st.st_mode)) - info.type = FileInfo::Type::REGULAR; + info.type = StorageFileInfo::Type::REGULAR; else if (S_ISDIR(st.st_mode)) - info.type = FileInfo::Type::DIRECTORY; + info.type = StorageFileInfo::Type::DIRECTORY; else - info.type = FileInfo::Type::OTHER; + info.type = StorageFileInfo::Type::OTHER; info.size = st.st_size; info.mtime = st.st_mtime; @@ -262,11 +262,11 @@ Copy(FileInfo &info, const struct stat &st) class NfsGetInfoOperation final : public BlockingNfsOperation { const char *const path; - FileInfo &info; + StorageFileInfo &info; public: NfsGetInfoOperation(NfsConnection &_connection, const char *_path, - FileInfo &_info) + StorageFileInfo &_info) :BlockingNfsOperation(_connection), path(_path), info(_info) {} protected: @@ -281,7 +281,7 @@ protected: bool NfsStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow, - FileInfo &info, Error &error) + StorageFileInfo &info, Error &error) { const std::string path = UriToNfsPath(uri_utf8, error); if (path.empty()) @@ -304,19 +304,19 @@ SkipNameFS(const char *name) } static void -Copy(FileInfo &info, const struct nfsdirent &ent) +Copy(StorageFileInfo &info, const struct nfsdirent &ent) { switch (ent.type) { case NF3REG: - info.type = FileInfo::Type::REGULAR; + info.type = StorageFileInfo::Type::REGULAR; break; case NF3DIR: - info.type = FileInfo::Type::DIRECTORY; + info.type = StorageFileInfo::Type::DIRECTORY; break; default: - info.type = FileInfo::Type::OTHER; + info.type = StorageFileInfo::Type::OTHER; break; } diff --git a/src/storage/plugins/NfsStorage.hxx b/src/storage/plugins/NfsStorage.hxx index f7e18effc..bc757cf8c 100644 --- a/src/storage/plugins/NfsStorage.hxx +++ b/src/storage/plugins/NfsStorage.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/storage/plugins/SmbclientStorage.cxx b/src/storage/plugins/SmbclientStorage.cxx index 70a6e16bb..84b212cd1 100644 --- a/src/storage/plugins/SmbclientStorage.cxx +++ b/src/storage/plugins/SmbclientStorage.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -44,7 +44,8 @@ public: /* virtual methods from class StorageDirectoryReader */ const char *Read() override; - bool GetInfo(bool follow, FileInfo &info, Error &error) override; + bool GetInfo(bool follow, StorageFileInfo &info, + Error &error) override; }; class SmbclientStorage final : public Storage { @@ -63,7 +64,7 @@ public: } /* virtual methods from class Storage */ - bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + bool GetInfo(const char *uri_utf8, bool follow, StorageFileInfo &info, Error &error) override; StorageDirectoryReader *OpenDirectory(const char *uri_utf8, @@ -92,7 +93,7 @@ SmbclientStorage::MapToRelativeUTF8(const char *uri_utf8) const } static bool -GetInfo(const char *path, FileInfo &info, Error &error) +GetInfo(const char *path, StorageFileInfo &info, Error &error) { struct stat st; smbclient_mutex.lock(); @@ -104,11 +105,11 @@ GetInfo(const char *path, FileInfo &info, Error &error) } if (S_ISREG(st.st_mode)) - info.type = FileInfo::Type::REGULAR; + info.type = StorageFileInfo::Type::REGULAR; else if (S_ISDIR(st.st_mode)) - info.type = FileInfo::Type::DIRECTORY; + info.type = StorageFileInfo::Type::DIRECTORY; else - info.type = FileInfo::Type::OTHER; + info.type = StorageFileInfo::Type::OTHER; info.size = st.st_size; info.mtime = st.st_mtime; @@ -119,7 +120,7 @@ GetInfo(const char *path, FileInfo &info, Error &error) bool SmbclientStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow, - FileInfo &info, Error &error) + StorageFileInfo &info, Error &error) { const std::string mapped = MapUTF8(uri_utf8); return ::GetInfo(mapped.c_str(), info, error); @@ -172,7 +173,8 @@ SmbclientDirectoryReader::Read() } bool -SmbclientDirectoryReader::GetInfo(gcc_unused bool follow, FileInfo &info, +SmbclientDirectoryReader::GetInfo(gcc_unused bool follow, + StorageFileInfo &info, Error &error) { const std::string path = PathTraitsUTF8::Build(base.c_str(), name); diff --git a/src/storage/plugins/SmbclientStorage.hxx b/src/storage/plugins/SmbclientStorage.hxx index 7c198d920..dd047f97e 100644 --- a/src/storage/plugins/SmbclientStorage.hxx +++ b/src/storage/plugins/SmbclientStorage.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/ByteOrder.hxx b/src/system/ByteOrder.hxx index 42181fe2c..babc503a9 100644 --- a/src/system/ByteOrder.hxx +++ b/src/system/ByteOrder.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Max Kellermann <max@duempel.org>, + * Copyright (C) 2011-2015 Max Kellermann <max@duempel.org>, * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,6 +30,8 @@ #ifndef BYTE_ORDER_HXX #define BYTE_ORDER_HXX +#include "Compiler.h" + #include <stdint.h> #if defined(__i386__) || defined(__x86_64__) || defined(__ARMEL__) @@ -75,23 +77,53 @@ IsBigEndian() } static inline constexpr uint16_t +GenericByteSwap16(uint16_t value) +{ + return (value >> 8) | (value << 8); +} + +static inline constexpr uint32_t +GenericByteSwap32(uint32_t value) +{ + return (value >> 24) | ((value >> 8) & 0x0000ff00) | + ((value << 8) & 0x00ff0000) | (value << 24); +} + +static inline constexpr uint64_t +GenericByteSwap64(uint64_t value) +{ + return uint64_t(GenericByteSwap32(uint32_t(value >> 32))) + | (uint64_t(GenericByteSwap32(value)) << 32); +} + +static inline constexpr uint16_t ByteSwap16(uint16_t value) { - return (value >> 8) | (value << 8); +#if CLANG_OR_GCC_VERSION(4,8) + return __builtin_bswap16(value); +#else + return GenericByteSwap16(value); +#endif } static inline constexpr uint32_t ByteSwap32(uint32_t value) { - return (value >> 24) | ((value >> 8) & 0x0000ff00) | - ((value << 8) & 0x00ff0000) | (value << 24); +#if CLANG_OR_GCC_VERSION(4,3) + return __builtin_bswap32(value); +#else + return GenericByteSwap32(value); +#endif } static inline constexpr uint64_t ByteSwap64(uint64_t value) { - return uint64_t(ByteSwap32(uint32_t(value >> 32))) - | (uint64_t(ByteSwap32(value)) << 32); +#if CLANG_OR_GCC_VERSION(4,3) + return __builtin_bswap64(value); +#else + return GenericByteSwap64(value); +#endif } /** diff --git a/src/system/Clock.cxx b/src/system/Clock.cxx index c2f5e5087..161525fe1 100644 --- a/src/system/Clock.cxx +++ b/src/system/Clock.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/Clock.hxx b/src/system/Clock.hxx index 333a41000..7a710477e 100644 --- a/src/system/Clock.hxx +++ b/src/system/Clock.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/EPollFD.cxx b/src/system/EPollFD.cxx index 43e74712f..08051cf06 100644 --- a/src/system/EPollFD.cxx +++ b/src/system/EPollFD.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/EPollFD.hxx b/src/system/EPollFD.hxx index 8b9d7d2ba..d12561b5d 100644 --- a/src/system/EPollFD.hxx +++ b/src/system/EPollFD.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/EventFD.cxx b/src/system/EventFD.cxx index 9ac4c1d94..016dd372c 100644 --- a/src/system/EventFD.cxx +++ b/src/system/EventFD.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,46 +20,35 @@ #include "config.h" #ifdef USE_EVENTFD #include "EventFD.hxx" -#include "system/fd_util.h" #include "system/FatalError.hxx" #include "Compiler.h" #include <assert.h> -#include <unistd.h> - #include <sys/eventfd.h> EventFD::EventFD() - :fd(eventfd_cloexec_nonblock(0, 0)) { - if (fd < 0) + if (!fd.CreateEventFD(0)) FatalSystemError("eventfd() failed"); } -EventFD::~EventFD() -{ - assert(fd >= 0); - - close(fd); -} - bool EventFD::Read() { - assert(fd >= 0); + assert(fd.IsDefined()); eventfd_t value; - return read(fd, &value, sizeof(value)) == (ssize_t)sizeof(value); + return fd.Read(&value, sizeof(value)) == (ssize_t)sizeof(value); } void EventFD::Write() { - assert(fd >= 0); + assert(fd.IsDefined()); static constexpr eventfd_t value = 1; gcc_unused ssize_t nbytes = - write(fd, &value, sizeof(value)); + fd.Write(&value, sizeof(value)); } #endif /* USE_EVENTFD */ diff --git a/src/system/EventFD.hxx b/src/system/EventFD.hxx index 2a70461d9..616877f4a 100644 --- a/src/system/EventFD.hxx +++ b/src/system/EventFD.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,7 @@ #define MPD_EVENT_FD_HXX #include "check.h" +#include "FileDescriptor.hxx" /** * A class that wraps eventfd(). @@ -28,17 +29,19 @@ * Errors in the constructor are fatal. */ class EventFD { - int fd; + FileDescriptor fd; public: EventFD(); - ~EventFD(); + ~EventFD() { + fd.Close(); + } EventFD(const EventFD &other) = delete; EventFD &operator=(const EventFD &other) = delete; int Get() const { - return fd; + return fd.Get(); } /** diff --git a/src/system/EventPipe.cxx b/src/system/EventPipe.cxx index b8fc85aed..8b3141492 100644 --- a/src/system/EventPipe.cxx +++ b/src/system/EventPipe.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/EventPipe.hxx b/src/system/EventPipe.hxx index 42b3bb93d..229d5633e 100644 --- a/src/system/EventPipe.hxx +++ b/src/system/EventPipe.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/FatalError.cxx b/src/system/FatalError.cxx index 35e94f169..664b96a6c 100644 --- a/src/system/FatalError.cxx +++ b/src/system/FatalError.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,10 +23,6 @@ #include "util/Domain.hxx" #include "LogV.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <unistd.h> #include <stdarg.h> #include <stdio.h> @@ -78,18 +74,31 @@ FatalError(const char *msg, const Error &error) FormatFatalError("%s: %s", msg, error.GetMessage()); } +#ifdef WIN32 + +void +FatalSystemError(const char *msg, DWORD code) +{ + char buffer[256]; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, code, 0, + buffer, sizeof(buffer), nullptr); + FormatFatalError("%s: %s", msg, buffer); +} + +#endif + void FatalSystemError(const char *msg) { - const char *system_error; #ifdef WIN32 - system_error = g_win32_error_message(GetLastError()); + FatalSystemError(msg, GetLastError()); #else - system_error = strerror(errno); -#endif - + const char *system_error = strerror(errno); FormatError(fatal_error_domain, "%s: %s", msg, system_error); Abort(); +#endif } void diff --git a/src/system/FatalError.hxx b/src/system/FatalError.hxx index d4698b3d9..89363feeb 100644 --- a/src/system/FatalError.hxx +++ b/src/system/FatalError.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,6 +23,10 @@ #include "check.h" #include "Compiler.h" +#ifdef WIN32 +#include <windef.h> +#endif + class Error; /** @@ -53,6 +57,14 @@ gcc_noreturn void FatalSystemError(const char *msg); +#ifdef WIN32 + +gcc_noreturn +void +FatalSystemError(const char *msg, DWORD code); + +#endif + gcc_noreturn void FormatFatalSystemError(const char *fmt, ...); diff --git a/src/system/FileDescriptor.cxx b/src/system/FileDescriptor.cxx new file mode 100644 index 000000000..db258e107 --- /dev/null +++ b/src/system/FileDescriptor.cxx @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FileDescriptor.hxx" + +#include <sys/stat.h> +#include <fcntl.h> + +#ifndef WIN32 +#include <poll.h> +#endif + +#ifdef USE_EVENTFD +#include <sys/eventfd.h> +#endif + +#ifdef USE_SIGNALFD +#include <sys/signalfd.h> +#endif + +#ifdef HAVE_INOTIFY_INIT +#include <sys/inotify.h> +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +bool +FileDescriptor::Open(const char *pathname, int flags, mode_t mode) +{ + fd = ::open(pathname, flags | O_NOCTTY | O_CLOEXEC, mode); + return IsDefined(); +} + +bool +FileDescriptor::OpenReadOnly(const char *pathname) +{ + return Open(pathname, O_RDONLY); +} + +#ifndef WIN32 + +bool +FileDescriptor::OpenNonBlocking(const char *pathname) +{ + return Open(pathname, O_RDWR | O_NONBLOCK); +} + +bool +FileDescriptor::CreatePipe(FileDescriptor &r, FileDescriptor &w) +{ + int fds[2]; + +#ifdef HAVE_PIPE2 + const int flags = O_CLOEXEC; + const int result = pipe2(fds, flags); +#else + const int result = pipe(fds); +#endif + + if (result < 0) + return false; + + r = FileDescriptor(fds[0]); + w = FileDescriptor(fds[1]); + return true; +} + +void +FileDescriptor::SetNonBlocking() +{ + assert(IsDefined()); + + int flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +void +FileDescriptor::SetBlocking() +{ + assert(IsDefined()); + + int flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +} + +#endif + +#ifdef USE_EVENTFD + +bool +FileDescriptor::CreateEventFD(unsigned initval) +{ + fd = ::eventfd(initval, EFD_NONBLOCK|EFD_CLOEXEC); + return fd >= 0; +} + +#endif + +#ifdef USE_SIGNALFD + +bool +FileDescriptor::CreateSignalFD(const sigset_t *mask) +{ + int new_fd = ::signalfd(fd, mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (new_fd < 0) + return false; + + fd = new_fd; + return true; +} + +#endif + +#ifdef HAVE_INOTIFY_INIT + +bool +FileDescriptor::CreateInotify() +{ +#ifdef HAVE_INOTIFY_INIT1 + int new_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); +#else + int new_fd = inotify_init(); +#endif + if (new_fd < 0) + return false; + +#ifndef HAVE_INOTIFY_INIT1 + SetNonBlocking(); +#endif + + fd = new_fd; + return true; +} + +#endif + +bool +FileDescriptor::Rewind() +{ + assert(IsDefined()); + + return lseek(fd, 0, SEEK_SET) == 0; +} + +off_t +FileDescriptor::GetSize() const +{ + struct stat st; + return ::fstat(fd, &st) >= 0 + ? (long)st.st_size + : -1; +} + +#ifndef WIN32 + +int +FileDescriptor::Poll(short events, int timeout) const +{ + assert(IsDefined()); + + struct pollfd pfd; + pfd.fd = fd; + pfd.events = events; + int result = poll(&pfd, 1, timeout); + return result > 0 + ? pfd.revents + : result; +} + +int +FileDescriptor::WaitReadable(int timeout) const +{ + return Poll(POLLIN, timeout); +} + +int +FileDescriptor::WaitWritable(int timeout) const +{ + return Poll(POLLOUT, timeout); +} + +#endif diff --git a/src/system/FileDescriptor.hxx b/src/system/FileDescriptor.hxx new file mode 100644 index 000000000..75a76844c --- /dev/null +++ b/src/system/FileDescriptor.hxx @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FILE_DESCRIPTOR_HXX +#define FILE_DESCRIPTOR_HXX + +#include "check.h" +#include "Compiler.h" + +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> + +#ifdef USE_SIGNALFD +#include <signal.h> +#endif + +/** + * An OO wrapper for a UNIX file descriptor. + * + * This class is unmanaged and trivial. + */ +class FileDescriptor { +protected: + int fd; + +public: + FileDescriptor() = default; + explicit constexpr FileDescriptor(int _fd):fd(_fd) {} + + constexpr bool operator==(FileDescriptor other) const { + return fd == other.fd; + } + + constexpr bool IsDefined() const { + return fd >= 0; + } + + /** + * Returns the file descriptor. This may only be called if + * IsDefined() returns true. + */ + constexpr int Get() const { + return fd; + } + + void Set(int _fd) { + fd = _fd; + } + + int Steal() { + assert(IsDefined()); + + int _fd = fd; + fd = -1; + return _fd; + } + + void SetUndefined() { + fd = -1; + } + + static constexpr FileDescriptor Undefined() { + return FileDescriptor(-1); + } + + bool Open(const char *pathname, int flags, mode_t mode=0666); + bool OpenReadOnly(const char *pathname); + +#ifndef WIN32 + bool OpenNonBlocking(const char *pathname); + + static bool CreatePipe(FileDescriptor &r, FileDescriptor &w); + + /** + * Enable non-blocking mode on this file descriptor. + */ + void SetNonBlocking(); + + /** + * Enable blocking mode on this file descriptor. + */ + void SetBlocking(); + + /** + * Duplicate the file descriptor onto the given file descriptor. + */ + bool Duplicate(int new_fd) const { + return ::dup2(Get(), new_fd) == 0; + } +#endif + +#ifdef USE_EVENTFD + bool CreateEventFD(unsigned initval=0); +#endif + +#ifdef USE_SIGNALFD + bool CreateSignalFD(const sigset_t *mask); +#endif + +#ifdef HAVE_INOTIFY_INIT + bool CreateInotify(); +#endif + + /** + * Close the file descriptor. It is legal to call it on an + * "undefined" object. After this call, IsDefined() is guaranteed + * to return false, and this object may be reused. + */ + bool Close() { + return ::close(Steal()) == 0; + } + + /** + * Rewind the pointer to the beginning of the file. + */ + bool Rewind(); + + off_t Seek(off_t offset) { + return lseek(Get(), offset, SEEK_SET); + } + + gcc_pure + off_t Tell() const { + return lseek(Get(), 0, SEEK_CUR); + } + + /** + * Returns the size of the file in bytes, or -1 on error. + */ + gcc_pure + off_t GetSize() const; + + ssize_t Read(void *buffer, size_t length) { + return ::read(fd, buffer, length); + } + + ssize_t Write(const void *buffer, size_t length) { + return ::write(fd, buffer, length); + } + +#ifndef WIN32 + int Poll(short events, int timeout) const; + + int WaitReadable(int timeout) const; + int WaitWritable(int timeout) const; +#endif +}; + +#endif diff --git a/src/system/PeriodClock.hxx b/src/system/PeriodClock.hxx index 2c535fee9..a9db4cea0 100644 --- a/src/system/PeriodClock.hxx +++ b/src/system/PeriodClock.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/system/SignalFD.cxx b/src/system/SignalFD.cxx index 173a0cc8c..14193c893 100644 --- a/src/system/SignalFD.cxx +++ b/src/system/SignalFD.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -23,33 +23,29 @@ #include "FatalError.hxx" #include <assert.h> -#include <unistd.h> #include <sys/signalfd.h> void SignalFD::Create(const sigset_t &mask) { - fd = ::signalfd(fd, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd < 0) + if (!fd.CreateSignalFD(&mask)) FatalSystemError("signalfd() failed"); } void SignalFD::Close() { - if (fd >= 0) { - ::close(fd); - fd = -1; - } + if (fd.IsDefined()) + fd.Close(); } int SignalFD::Read() { - assert(fd >= 0); + assert(fd.IsDefined()); signalfd_siginfo info; - return read(fd, &info, sizeof(info)) > 0 + return fd.Read(&info, sizeof(info)) > 0 ? info.ssi_signo : -1; } diff --git a/src/system/SignalFD.hxx b/src/system/SignalFD.hxx index 11bf30f74..dae150fea 100644 --- a/src/system/SignalFD.hxx +++ b/src/system/SignalFD.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,7 @@ #define MPD_SIGNAL_FD_HXX #include "check.h" +#include "FileDescriptor.hxx" #include <signal.h> @@ -28,7 +29,7 @@ * A class that wraps signalfd(). */ class SignalFD { - int fd; + FileDescriptor fd; public: SignalFD():fd(-1) {} @@ -48,7 +49,7 @@ public: void Close(); int Get() const { - return fd; + return fd.Get(); } /** diff --git a/src/system/fd_util.c b/src/system/fd_util.c index b53ecda00..5763ede90 100644 --- a/src/system/fd_util.c +++ b/src/system/fd_util.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * Redistribution and use in source and binary forms, with or without @@ -41,10 +41,6 @@ #include <sys/socket.h> #endif -#ifdef HAVE_INOTIFY_INIT -#include <sys/inotify.h> -#endif - #ifdef USE_EVENTFD #include <sys/eventfd.h> #endif @@ -106,16 +102,6 @@ fd_set_nonblock(int fd) } int -dup_cloexec(int oldfd) -{ - int newfd = dup(oldfd); - if (newfd >= 0) - fd_set_nonblock(newfd); - - return newfd; -} - -int open_cloexec(const char *path_fs, int flags, int mode) { int fd; @@ -136,30 +122,6 @@ open_cloexec(const char *path_fs, int flags, int mode) } int -pipe_cloexec(int fd[2]) -{ -#ifdef WIN32 - return _pipe(fd, 512, _O_BINARY); -#else - int ret; - -#ifdef HAVE_PIPE2 - ret = pipe2(fd, O_CLOEXEC); - if (ret >= 0 || errno != ENOSYS) - return ret; -#endif - - ret = pipe(fd); - if (ret >= 0) { - fd_set_cloexec(fd[0], true); - fd_set_cloexec(fd[1], true); - } - - return ret; -#endif -} - -int pipe_cloexec_nonblock(int fd[2]) { #ifdef WIN32 @@ -186,53 +148,6 @@ pipe_cloexec_nonblock(int fd[2]) #endif } -#ifndef WIN32 - -int -socketpair_cloexec(int domain, int type, int protocol, int sv[2]) -{ - int ret; - -#ifdef SOCK_CLOEXEC - ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); - if (ret >= 0 || errno != EINVAL) - return ret; -#endif - - ret = socketpair(domain, type, protocol, sv); - if (ret >= 0) { - fd_set_cloexec(sv[0], true); - fd_set_cloexec(sv[1], true); - } - - return ret; -} - -int -socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]) -{ - int ret; - -#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) - ret = socketpair(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol, - sv); - if (ret >= 0 || errno != EINVAL) - return ret; -#endif - - ret = socketpair(domain, type, protocol, sv); - if (ret >= 0) { - fd_set_cloexec(sv[0], true); - fd_set_nonblock(sv[0]); - fd_set_cloexec(sv[1], true); - fd_set_nonblock(sv[1]); - } - - return ret; -} - -#endif - int socket_cloexec_nonblock(int domain, int type, int protocol) { @@ -281,65 +196,6 @@ accept_cloexec_nonblock(int fd, struct sockaddr *address, return ret; } -#ifndef WIN32 - -ssize_t -recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) -{ -#ifdef MSG_CMSG_CLOEXEC - flags |= MSG_CMSG_CLOEXEC; -#endif - - ssize_t result = recvmsg(sockfd, msg, flags); - if (result >= 0) { - struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); - while (cmsg != NULL) { - if (cmsg->cmsg_type == SCM_RIGHTS) { - const int *fd_p = (const int *)CMSG_DATA(cmsg); - fd_set_cloexec(*fd_p, true); - } - - cmsg = CMSG_NXTHDR(msg, cmsg); - } - } - - return result; -} - -#endif - -#ifdef HAVE_INOTIFY_INIT - -int -inotify_init_cloexec(void) -{ - int fd; - -#ifdef HAVE_INOTIFY_INIT1 - fd = inotify_init1(IN_CLOEXEC); - if (fd >= 0 || errno != ENOSYS) - return fd; -#endif - - fd = inotify_init(); - if (fd >= 0) - fd_set_cloexec(fd, true); - - return fd; -} - -#endif - -#ifdef USE_EVENTFD - -int -eventfd_cloexec_nonblock(unsigned initval, int flags) -{ - return eventfd(initval, flags | EFD_CLOEXEC | EFD_NONBLOCK); -} - -#endif - int close_socket(int fd) { diff --git a/src/system/fd_util.h b/src/system/fd_util.h index f4a940e91..172b1ade3 100644 --- a/src/system/fd_util.h +++ b/src/system/fd_util.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * Redistribution and use in source and binary forms, with or without @@ -55,13 +55,6 @@ int fd_set_cloexec(int fd, bool enable); /** - * Wrapper for dup(), which sets the CLOEXEC flag on the new - * descriptor. - */ -int -dup_cloexec(int oldfd); - -/** * Wrapper for open(), which sets the CLOEXEC flag (atomically if * supported by the OS). */ @@ -71,13 +64,6 @@ open_cloexec(const char *path_fs, int flags, int mode); /** * Wrapper for pipe(), which sets the CLOEXEC flag (atomically if * supported by the OS). - */ -int -pipe_cloexec(int fd[2]); - -/** - * Wrapper for pipe(), which sets the CLOEXEC flag (atomically if - * supported by the OS). * * On systems that supports it (everybody except for Windows), it also * sets the NONBLOCK flag. @@ -85,25 +71,7 @@ pipe_cloexec(int fd[2]); int pipe_cloexec_nonblock(int fd[2]); -#ifndef WIN32 - -/** - * Wrapper for socketpair(), which sets the CLOEXEC flag (atomically - * if supported by the OS). - */ -int -socketpair_cloexec(int domain, int type, int protocol, int sv[2]); - -/** - * Wrapper for socketpair(), which sets the flags CLOEXEC and NONBLOCK - * (atomically if supported by the OS). - */ -int -socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]); - -#endif - -#ifdef HAVE_LIBMPDCLIENT +#ifdef ENABLE_LIBMPDCLIENT /* Avoid symbol conflict with statically linked libmpdclient */ #define socket_cloexec_nonblock socket_cloexec_nonblock_noconflict #endif @@ -123,42 +91,6 @@ int accept_cloexec_nonblock(int fd, struct sockaddr *address, size_t *address_length_r); - -#ifndef WIN32 - -struct msghdr; - -/** - * Wrapper for recvmsg(), which sets the CLOEXEC flag (atomically if - * supported by the OS). - */ -ssize_t -recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); - -#endif - -#ifdef HAVE_INOTIFY_INIT - -/** - * Wrapper for inotify_init(), which sets the CLOEXEC flag (atomically - * if supported by the OS). - */ -int -inotify_init_cloexec(void); - -#endif - -#ifdef USE_EVENTFD - -/** - * Wrapper for eventfd() which sets the flags CLOEXEC and NONBLOCK - * flag (atomically if supported by the OS). - */ -int -eventfd_cloexec_nonblock(unsigned initval, int flags); - -#endif - /** * Portable wrapper for close(); use closesocket() on WIN32/WinSock. */ diff --git a/src/tag/Aiff.cxx b/src/tag/Aiff.cxx index c2498c9e9..7235b76a8 100644 --- a/src/tag/Aiff.cxx +++ b/src/tag/Aiff.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/Aiff.hxx b/src/tag/Aiff.hxx index cd323ee2e..f9b11b5a6 100644 --- a/src/tag/Aiff.hxx +++ b/src/tag/Aiff.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/ApeLoader.cxx b/src/tag/ApeLoader.cxx index f51cb5c0b..b1759a730 100644 --- a/src/tag/ApeLoader.cxx +++ b/src/tag/ApeLoader.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,7 @@ #include "ApeLoader.hxx" #include "system/ByteOrder.hxx" #include "fs/FileSystem.hxx" +#include "util/StringView.hxx" #include <stdint.h> #include <assert.h> @@ -89,7 +90,7 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback) if (remaining < size) break; - if (!callback(flags, key, p, size)) + if (!callback(flags, key, {p, size})) break; p += size; @@ -103,7 +104,7 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback) bool tag_ape_scan(Path path_fs, ApeTagCallback callback) { - FILE *fp = FOpen(path_fs, "rb"); + FILE *fp = FOpen(path_fs, PATH_LITERAL("rb")); if (fp == nullptr) return false; diff --git a/src/tag/ApeLoader.hxx b/src/tag/ApeLoader.hxx index ce82cc35d..4587ff063 100644 --- a/src/tag/ApeLoader.hxx +++ b/src/tag/ApeLoader.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,11 +26,11 @@ #include <stddef.h> +struct StringView; class Path; typedef std::function<bool(unsigned long flags, const char *key, - const char *value, - size_t value_length)> ApeTagCallback; + StringView value)> ApeTagCallback; /** * Scans the APE tag values from a file. diff --git a/src/tag/ApeReplayGain.cxx b/src/tag/ApeReplayGain.cxx index 345f45710..883885369 100644 --- a/src/tag/ApeReplayGain.cxx +++ b/src/tag/ApeReplayGain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,15 +21,16 @@ #include "ApeReplayGain.hxx" #include "ApeLoader.hxx" #include "ReplayGain.hxx" -#include "util/ASCII.hxx" #include "fs/Path.hxx" +#include "util/ASCII.hxx" +#include "util/StringView.hxx" #include <string.h> #include <stdlib.h> static bool replay_gain_ape_callback(unsigned long flags, const char *key, - const char *_value, size_t value_length, + StringView _value, ReplayGainInfo &info) { /* we only care about utf-8 text tags */ @@ -37,11 +38,11 @@ replay_gain_ape_callback(unsigned long flags, const char *key, return false; char value[16]; - if (value_length >= sizeof(value)) + if (_value.size >= sizeof(value)) return false; - memcpy(value, _value, value_length); - value[value_length] = 0; + memcpy(value, _value.data, _value.size); + value[_value.size] = 0; return ParseReplayGainTag(info, key, value); } @@ -53,10 +54,9 @@ replay_gain_ape_read(Path path_fs, ReplayGainInfo &info) auto callback = [&info, &found] (unsigned long flags, const char *key, - const char *value, - size_t value_length) { + StringView value) { found |= replay_gain_ape_callback(flags, key, - value, value_length, + value, info); return true; }; diff --git a/src/tag/ApeReplayGain.hxx b/src/tag/ApeReplayGain.hxx index 03c899c5c..faf68f0e3 100644 --- a/src/tag/ApeReplayGain.hxx +++ b/src/tag/ApeReplayGain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/ApeTag.cxx b/src/tag/ApeTag.cxx index f714a1624..81318a771 100644 --- a/src/tag/ApeTag.cxx +++ b/src/tag/ApeTag.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,13 +24,13 @@ #include "TagTable.hxx" #include "TagHandler.hxx" #include "fs/Path.hxx" +#include "util/StringView.hxx" #include <string> #include <string.h> const struct tag_table ape_tags[] = { - { "album artist", TAG_ALBUM_ARTIST }, { "year", TAG_DATE }, { nullptr, TAG_NUM_OF_ITEM_TYPES } }; @@ -76,17 +76,18 @@ ForEachValue(const char *value, const char *end, C &&callback) */ static bool tag_ape_import_item(unsigned long flags, - const char *key, const char *value, size_t value_length, + const char *key, StringView value, const struct tag_handler *handler, void *handler_ctx) { /* we only care about utf-8 text tags */ if ((flags & (0x3 << 1)) != 0) return false; - const char *const end = value + value_length; + const auto begin = value.begin(); + const auto end = value.end(); if (handler->pair != nullptr) - ForEachValue(value, end, [handler, handler_ctx, + ForEachValue(begin, end, [handler, handler_ctx, key](const char *_value) { handler->pair(key, _value, handler_ctx); }); @@ -95,8 +96,8 @@ tag_ape_import_item(unsigned long flags, if (type == TAG_NUM_OF_ITEM_TYPES) return false; - ForEachValue(value, end, [handler, handler_ctx, - type](const char *_value) { + ForEachValue(begin, end, [handler, handler_ctx, + type](const char *_value) { tag_handler_invoke_tag(handler, handler_ctx, type, _value); }); @@ -112,10 +113,8 @@ tag_ape_scan2(Path path_fs, auto callback = [handler, handler_ctx, &recognized] (unsigned long flags, const char *key, - const char *value, - size_t value_length) { + StringView value) { recognized |= tag_ape_import_item(flags, key, value, - value_length, handler, handler_ctx); return true; }; diff --git a/src/tag/ApeTag.hxx b/src/tag/ApeTag.hxx index edebf076c..20d1d7b81 100644 --- a/src/tag/ApeTag.hxx +++ b/src/tag/ApeTag.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/Format.cxx b/src/tag/Format.cxx new file mode 100644 index 000000000..de4db57ef --- /dev/null +++ b/src/tag/Format.cxx @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2003-2015 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 "Format.hxx" +#include "Tag.hxx" +#include "util/format.h" +#include "util/StringUtil.hxx" + +#include <algorithm> + +#include <string.h> +#include <time.h> + +struct FormatTagContext { + const Tag &tag; + + char buffer[256]; + + explicit FormatTagContext(const Tag &_tag):tag(_tag) {} +}; + +/** + * Is this a character unsafe to use in a path name segment? + */ +static constexpr bool +IsUnsafeChar(char ch) +{ + return + /* disallow characters illegal in file names on + Windows (Linux allows almost anything) */ + ch == '\\' || ch == '/' || ch == ':' || ch == '*' || + ch == '?' || ch == '<' || ch == '>' || ch == '|' || + /* allow space, but disallow all other whitespace */ + (unsigned char)ch < 0x20; +} + +gcc_pure +static bool +HasUnsafeChar(const char *s) +{ + for (; *s; ++s) + if (IsUnsafeChar(*s)) + return true; + + return false; +} + +static const char * +SanitizeString(const char *s, char *buffer, size_t buffer_size) +{ + /* skip leading dots to avoid generating "../" sequences */ + while (*s == '.') + ++s; + + if (!HasUnsafeChar(s)) + return s; + + char *end = CopyString(buffer, s, buffer_size); + std::replace_if(buffer, end, IsUnsafeChar, ' '); + return buffer; +} + +gcc_pure gcc_nonnull_all +static const char * +TagGetter(const void *object, const char *name) +{ + const auto &_ctx = *(const FormatTagContext *)object; + auto &ctx = const_cast<FormatTagContext &>(_ctx); + + if (strcmp(name, "iso8601") == 0) { + time_t t = time(nullptr); +#ifdef WIN32 + const struct tm *tm2 = gmtime(&t); +#else + struct tm tm; + const struct tm *tm2 = gmtime_r(&t, &tm); +#endif + if (tm2 == nullptr) + return ""; + + strftime(ctx.buffer, sizeof(ctx.buffer), +#ifdef WIN32 + /* kludge: use underscore instead of colon on + Windows because colons are not allowed in + file names, and this library is mostly + used to generate file names */ + "%Y-%m-%dT%H_%M_%SZ", +#else + "%FT%TZ", +#endif + tm2); + return ctx.buffer; + } + + const Tag &tag = ctx.tag; + + TagType tag_type = tag_name_parse_i(name); + if (tag_type == TAG_NUM_OF_ITEM_TYPES) + /* unknown tag name */ + return nullptr; + + const char *value = tag.GetValue(tag_type); + if (value == nullptr) + /* known tag name, but not present in this object */ + value = ""; + + // TODO: handle multiple tag values + return SanitizeString(value, ctx.buffer, sizeof(ctx.buffer)); +} + +char * +FormatTag(const Tag &tag, const char *format) +{ + FormatTagContext ctx(tag); + return format_object(format, &ctx, TagGetter); +} diff --git a/src/tag/Format.hxx b/src/tag/Format.hxx new file mode 100644 index 000000000..a08e687d0 --- /dev/null +++ b/src/tag/Format.hxx @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2003-2015 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_TAG_FORMAT_HXX +#define MPD_TAG_FORMAT_HXX + +#include "check.h" +#include "Compiler.h" + +struct Tag; + +gcc_malloc gcc_nonnull_all +char * +FormatTag(const Tag &tag, const char *format); + +#endif diff --git a/src/tag/Mask.hxx b/src/tag/Mask.hxx new file mode 100644 index 000000000..9576d56e7 --- /dev/null +++ b/src/tag/Mask.hxx @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2003-2015 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_TAG_MASK_HXX +#define MPD_TAG_MASK_HXX + +#include <stdint.h> + +typedef uint_least32_t tag_mask_t; + +#endif diff --git a/src/tag/MixRamp.cxx b/src/tag/MixRamp.cxx index e1b6e43c5..cbec047de 100644 --- a/src/tag/MixRamp.cxx +++ b/src/tag/MixRamp.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/MixRamp.hxx b/src/tag/MixRamp.hxx index 5b4e2dc30..7255bac4d 100644 --- a/src/tag/MixRamp.hxx +++ b/src/tag/MixRamp.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/ReplayGain.cxx b/src/tag/ReplayGain.cxx index 83a48f243..edf8c92f1 100644 --- a/src/tag/ReplayGain.cxx +++ b/src/tag/ReplayGain.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/ReplayGain.hxx b/src/tag/ReplayGain.hxx index 2bf5e0db1..a2e897235 100644 --- a/src/tag/ReplayGain.hxx +++ b/src/tag/ReplayGain.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/Riff.cxx b/src/tag/Riff.cxx index c630f082d..741225813 100644 --- a/src/tag/Riff.cxx +++ b/src/tag/Riff.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/Riff.hxx b/src/tag/Riff.hxx index a9af67b7a..b15716882 100644 --- a/src/tag/Riff.hxx +++ b/src/tag/Riff.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/Set.cxx b/src/tag/Set.cxx index 6a55a450f..6499321e6 100644 --- a/src/tag/Set.cxx +++ b/src/tag/Set.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "Set.hxx" #include "TagBuilder.hxx" -#include "TagSettings.h" +#include "Settings.hxx" #include <assert.h> @@ -58,16 +58,16 @@ CopyTagItem(TagBuilder &dest, const Tag &src, TagType type) * Copy all tag items of the types in the mask. */ static void -CopyTagMask(TagBuilder &dest, const Tag &src, uint32_t mask) +CopyTagMask(TagBuilder &dest, const Tag &src, tag_mask_t mask) { for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) - if ((mask & (1u << i)) != 0) + if ((mask & (tag_mask_t(1) << i)) != 0) CopyTagItem(dest, src, TagType(i)); } void TagSet::InsertUnique(const Tag &src, TagType type, const char *value, - uint32_t group_mask) + tag_mask_t group_mask) { TagBuilder builder; if (value == nullptr) @@ -75,7 +75,7 @@ TagSet::InsertUnique(const Tag &src, TagType type, const char *value, else builder.AddItem(type, value); CopyTagMask(builder, src, group_mask); -#if defined(__clang__) || GCC_CHECK_VERSION(4,8) +#if CLANG_OR_GCC_VERSION(4,8) emplace(builder.Commit()); #else insert(builder.Commit()); @@ -85,7 +85,7 @@ TagSet::InsertUnique(const Tag &src, TagType type, const char *value, bool TagSet::CheckUnique(TagType dest_type, const Tag &tag, TagType src_type, - uint32_t group_mask) + tag_mask_t group_mask) { bool found = false; @@ -101,16 +101,16 @@ TagSet::CheckUnique(TagType dest_type, void TagSet::InsertUnique(const Tag &tag, - TagType type, uint32_t group_mask) + TagType type, tag_mask_t group_mask) { static_assert(sizeof(group_mask) * 8 >= TAG_NUM_OF_ITEM_TYPES, "Mask is too small"); - assert((group_mask & (1u << unsigned(type))) == 0); + assert((group_mask & (tag_mask_t(1) << unsigned(type))) == 0); if (!CheckUnique(type, tag, type, group_mask) && (type != TAG_ALBUM_ARTIST || - ignore_tag_items[TAG_ALBUM_ARTIST] || + !IsTagEnabled(TAG_ALBUM_ARTIST) || /* fall back to "Artist" if no "AlbumArtist" was found */ !CheckUnique(type, tag, TAG_ARTIST, group_mask))) InsertUnique(tag, type, nullptr, group_mask); diff --git a/src/tag/Set.hxx b/src/tag/Set.hxx index b5acfcb36..587b25a3a 100644 --- a/src/tag/Set.hxx +++ b/src/tag/Set.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,11 +22,11 @@ #include "Compiler.h" #include "Tag.hxx" +#include "Mask.hxx" #include <set> #include <string.h> -#include <stdint.h> /** * Helper class for #TagSet which compares two #Tag objects. @@ -59,15 +59,15 @@ struct TagLess { class TagSet : public std::set<Tag, TagLess> { public: void InsertUnique(const Tag &tag, - TagType type, uint32_t group_mask); + TagType type, tag_mask_t group_mask); private: void InsertUnique(const Tag &src, TagType type, const char *value, - uint32_t group_mask); + tag_mask_t group_mask); bool CheckUnique(TagType dest_type, const Tag &tag, TagType src_type, - uint32_t group_mask); + tag_mask_t group_mask); }; #endif diff --git a/src/tag/Settings.cxx b/src/tag/Settings.cxx new file mode 100644 index 000000000..83c626db8 --- /dev/null +++ b/src/tag/Settings.cxx @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2003-2015 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 "Settings.hxx" + +tag_mask_t global_tag_mask = (tag_mask_t)-1 & ~(1 << TAG_COMMENT); diff --git a/src/tag/Settings.hxx b/src/tag/Settings.hxx new file mode 100644 index 000000000..332454b8f --- /dev/null +++ b/src/tag/Settings.hxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2003-2015 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_TAG_SETTINGS_HXX +#define MPD_TAG_SETTINGS_HXX + +#include "Mask.hxx" +#include "TagType.h" +#include "Compiler.h" + +#include <stdint.h> + +extern tag_mask_t global_tag_mask; + +gcc_const +static inline bool +IsTagEnabled(unsigned tag) +{ + return global_tag_mask & (1u << tag); +} + +gcc_const +static inline bool +IsTagEnabled(TagType tag) +{ + return IsTagEnabled(unsigned(tag)); +} + +#endif diff --git a/src/tag/Tag.cxx b/src/tag/Tag.cxx index 92ac4214c..fc124df0d 100644 --- a/src/tag/Tag.cxx +++ b/src/tag/Tag.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,7 +21,6 @@ #include "Tag.hxx" #include "TagPool.hxx" #include "TagString.hxx" -#include "TagSettings.h" #include "TagBuilder.hxx" #include "util/ASCII.hxx" diff --git a/src/tag/Tag.hxx b/src/tag/Tag.hxx index f1d3d5767..aca2bf6a0 100644 --- a/src/tag/Tag.hxx +++ b/src/tag/Tag.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagBuilder.cxx b/src/tag/TagBuilder.cxx index 0882f9561..82d99006a 100644 --- a/src/tag/TagBuilder.cxx +++ b/src/tag/TagBuilder.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,11 +19,12 @@ #include "config.h" #include "TagBuilder.hxx" -#include "TagSettings.h" +#include "Settings.hxx" #include "TagPool.hxx" #include "TagString.hxx" #include "Tag.hxx" #include "util/WritableBuffer.hxx" +#include "util/StringView.hxx" #include <array> @@ -189,22 +190,16 @@ TagBuilder::Complement(const Tag &other) } inline void -TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) +TagBuilder::AddItemInternal(TagType type, StringView value) { -#if !CLANG_CHECK_VERSION(3,6) - /* disabled on clang due to -Wtautological-pointer-compare */ - assert(value != nullptr); -#endif - assert(length > 0); + assert(!value.IsEmpty()); - auto f = FixTagString(value, length); - if (!f.IsNull()) { - value = f.data; - length = f.size; - } + auto f = FixTagString(value); + if (!f.IsNull()) + value = { f.data, f.size }; tag_pool_lock.lock(); - auto i = tag_pool_get_item(type, value, length); + auto i = tag_pool_get_item(type, value); tag_pool_lock.unlock(); free(f.data); @@ -213,17 +208,12 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length) } void -TagBuilder::AddItem(TagType type, const char *value, size_t length) +TagBuilder::AddItem(TagType type, StringView value) { -#if !CLANG_CHECK_VERSION(3,6) - /* disabled on clang due to -Wtautological-pointer-compare */ - assert(value != nullptr); -#endif - - if (length == 0 || ignore_tag_items[type]) + if (value.IsEmpty() || !IsTagEnabled(type)) return; - AddItemInternal(type, value, length); + AddItemInternal(type, value); } void @@ -234,14 +224,14 @@ TagBuilder::AddItem(TagType type, const char *value) assert(value != nullptr); #endif - AddItem(type, value, strlen(value)); + AddItem(type, StringView(value)); } void TagBuilder::AddEmptyItem(TagType type) { tag_pool_lock.lock(); - auto i = tag_pool_get_item(type, "", 0); + auto i = tag_pool_get_item(type, StringView::Empty()); tag_pool_lock.unlock(); items.push_back(i); diff --git a/src/tag/TagBuilder.hxx b/src/tag/TagBuilder.hxx index aff581313..08c66d0b3 100644 --- a/src/tag/TagBuilder.hxx +++ b/src/tag/TagBuilder.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -28,6 +28,7 @@ #include <stddef.h> +struct StringView; struct TagItem; struct Tag; @@ -138,10 +139,10 @@ public: * * @param type the type of the new tag item * @param value the value of the tag item (not null-terminated) - * @param len the length of #value + * @param length the length of #value */ gcc_nonnull_all - void AddItem(TagType type, const char *value, size_t length); + void AddItem(TagType type, StringView value); /** * Appends a new tag item. @@ -171,7 +172,7 @@ public: private: gcc_nonnull_all - void AddItemInternal(TagType type, const char *value, size_t length); + void AddItemInternal(TagType type, StringView value); }; #endif diff --git a/src/tag/TagConfig.cxx b/src/tag/TagConfig.cxx index 00f20d1c0..b0e7b8ff0 100644 --- a/src/tag/TagConfig.cxx +++ b/src/tag/TagConfig.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -19,7 +19,7 @@ #include "config.h" #include "TagConfig.hxx" -#include "TagSettings.h" +#include "Settings.hxx" #include "Tag.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" @@ -35,11 +35,11 @@ void TagLoadConfig() { - const char *value = config_get_string(CONF_METADATA_TO_USE, nullptr); + const char *value = config_get_string(ConfigOption::METADATA_TO_USE); if (value == nullptr) return; - std::fill_n(ignore_tag_items, size_t(TAG_NUM_OF_ITEM_TYPES), true); + global_tag_mask = 0; if (StringEqualsCaseASCII(value, "none")) return; @@ -62,7 +62,7 @@ TagLoadConfig() FormatFatalError("error parsing metadata item \"%s\"", c); - ignore_tag_items[type] = false; + global_tag_mask |= tag_mask_t(1) << unsigned(type); s++; c = s; diff --git a/src/tag/TagConfig.hxx b/src/tag/TagConfig.hxx index 0088e9757..05c6594c6 100644 --- a/src/tag/TagConfig.hxx +++ b/src/tag/TagConfig.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagHandler.cxx b/src/tag/TagHandler.cxx index 2cbb83242..bbd30877a 100644 --- a/src/tag/TagHandler.cxx +++ b/src/tag/TagHandler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,9 @@ #include "TagBuilder.hxx" #include "util/ASCII.hxx" +#include <stdio.h> +#include <stdlib.h> + static void add_tag_duration(SongTime duration, void *ctx) { @@ -35,7 +38,17 @@ add_tag_tag(TagType type, const char *value, void *ctx) { TagBuilder &tag = *(TagBuilder *)ctx; - tag.AddItem(type, value); + if (type == TAG_TRACK || type == TAG_DISC) { + /* filter out this extra data and leading zeroes */ + char *end; + unsigned n = strtoul(value, &end, 10); + if (value != end) { + char s[21]; + if (snprintf(s, 21, "%u", n) >= 0) + tag.AddItem(type, s); + } + } else + tag.AddItem(type, value); } const struct tag_handler add_tag_handler = { diff --git a/src/tag/TagHandler.hxx b/src/tag/TagHandler.hxx index c12b605bc..e87c299fc 100644 --- a/src/tag/TagHandler.hxx +++ b/src/tag/TagHandler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagId3.cxx b/src/tag/TagId3.cxx index 02dc58364..288f239d1 100644 --- a/src/tag/TagId3.cxx +++ b/src/tag/TagId3.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,10 +33,6 @@ #include "fs/Path.hxx" #include "fs/FileSystem.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <id3tag.h> #include <string> @@ -66,12 +62,14 @@ static constexpr Domain id3_domain("id3"); +gcc_pure static inline bool tag_is_id3v1(struct id3_tag *tag) { return (id3_tag_options(tag, 0, 0) & ID3_TAG_OPTION_ID3V1) != 0; } +gcc_pure static id3_utf8_t * tag_id3_getstring(const struct id3_frame *frame, unsigned i) { @@ -89,42 +87,11 @@ tag_id3_getstring(const struct id3_frame *frame, unsigned i) /* This will try to convert a string to utf-8, */ static id3_utf8_t * -import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4) +import_id3_string(const id3_ucs4_t *ucs4) { - id3_utf8_t *utf8; - -#ifdef HAVE_GLIB - /* use encoding field here? */ - const char *encoding; - if (is_id3v1 && - (encoding = config_get_string(CONF_ID3V1_ENCODING, nullptr)) != nullptr) { - id3_latin1_t *isostr = id3_ucs4_latin1duplicate(ucs4); - if (gcc_unlikely(isostr == nullptr)) - return nullptr; - - utf8 = (id3_utf8_t *) - g_convert_with_fallback((const char*)isostr, -1, - "utf-8", encoding, - nullptr, nullptr, - nullptr, nullptr); - if (utf8 == nullptr) { - FormatWarning(id3_domain, - "Unable to convert %s string to UTF-8: '%s'", - encoding, isostr); - free(isostr); - return nullptr; - } - free(isostr); - } else { -#else - (void)is_id3v1; -#endif - utf8 = id3_ucs4_utf8duplicate(ucs4); - if (gcc_unlikely(utf8 == nullptr)) - return nullptr; -#ifdef HAVE_GLIB - } -#endif + id3_utf8_t *utf8 = id3_ucs4_utf8duplicate(ucs4); + if (gcc_unlikely(utf8 == nullptr)) + return nullptr; id3_utf8_t *utf8_stripped = (id3_utf8_t *) xstrdup(Strip((char *)utf8)); @@ -141,7 +108,7 @@ import_id3_string(bool is_id3v1, const id3_ucs4_t *ucs4) * - string list */ static void -tag_id3_import_text_frame(struct id3_tag *tag, const struct id3_frame *frame, +tag_id3_import_text_frame(const struct id3_frame *frame, TagType type, const struct tag_handler *handler, void *handler_ctx) { @@ -170,7 +137,7 @@ tag_id3_import_text_frame(struct id3_tag *tag, const struct id3_frame *frame, if (type == TAG_GENRE) ucs4 = id3_genre_name(ucs4); - id3_utf8_t *utf8 = import_id3_string(tag_is_id3v1(tag), ucs4); + id3_utf8_t *utf8 = import_id3_string(ucs4); if (utf8 == nullptr) continue; @@ -191,7 +158,7 @@ tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type, const struct id3_frame *frame; for (unsigned i = 0; (frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i) - tag_id3_import_text_frame(tag, frame, type, + tag_id3_import_text_frame(frame, type, handler, handler_ctx); } @@ -205,8 +172,7 @@ tag_id3_import_text(struct id3_tag *tag, const char *id, TagType type, * - full string (we use this one) */ static void -tag_id3_import_comment_frame(struct id3_tag *tag, - const struct id3_frame *frame, TagType type, +tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type, const struct tag_handler *handler, void *handler_ctx) { @@ -222,7 +188,7 @@ tag_id3_import_comment_frame(struct id3_tag *tag, if (ucs4 == nullptr) return; - id3_utf8_t *utf8 = import_id3_string(tag_is_id3v1(tag), ucs4); + id3_utf8_t *utf8 = import_id3_string(ucs4); if (utf8 == nullptr) return; @@ -241,7 +207,7 @@ tag_id3_import_comment(struct id3_tag *tag, const char *id, TagType type, const struct id3_frame *frame; for (unsigned i = 0; (frame = id3_tag_findframe(tag, id, i)) != nullptr; ++i) - tag_id3_import_comment_frame(tag, frame, type, + tag_id3_import_comment_frame(frame, type, handler, handler_ctx); } @@ -249,10 +215,11 @@ tag_id3_import_comment(struct id3_tag *tag, const char *id, TagType type, * Parse a TXXX name, and convert it to a TagType enum value. * Returns TAG_NUM_OF_ITEM_TYPES if the TXXX name is not understood. */ +gcc_pure static TagType tag_id3_parse_txxx_name(const char *name) { - static const struct tag_table txxx_tags[] = { + static constexpr struct tag_table txxx_tags[] = { { "ALBUMARTISTSORT", TAG_ALBUM_ARTIST_SORT }, { "MusicBrainz Artist Id", TAG_MUSICBRAINZ_ARTISTID }, { "MusicBrainz Album Id", TAG_MUSICBRAINZ_ALBUMID }, @@ -517,7 +484,7 @@ tag_id3_riff_aiff_load(FILE *file) struct id3_tag * tag_id3_load(Path path_fs, Error &error) { - FILE *file = FOpen(path_fs, "rb"); + FILE *file = FOpen(path_fs, PATH_LITERAL("rb")); if (file == nullptr) { error.FormatErrno("Failed to open file %s", path_fs.c_str()); return nullptr; diff --git a/src/tag/TagId3.hxx b/src/tag/TagId3.hxx index 1928d539d..94dfb1794 100644 --- a/src/tag/TagId3.hxx +++ b/src/tag/TagId3.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,7 +29,7 @@ struct Tag; struct id3_tag; class Error; -#ifdef HAVE_ID3TAG +#ifdef ENABLE_ID3TAG bool tag_id3_scan(Path path_fs, diff --git a/src/tag/TagItem.hxx b/src/tag/TagItem.hxx index 489ecde3a..0aa52f700 100644 --- a/src/tag/TagItem.hxx +++ b/src/tag/TagItem.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagNames.c b/src/tag/TagNames.c index e051c5863..056d714e4 100644 --- a/src/tag/TagNames.c +++ b/src/tag/TagNames.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagPool.cxx b/src/tag/TagPool.cxx index 29f605337..88b2ba333 100644 --- a/src/tag/TagPool.cxx +++ b/src/tag/TagPool.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,7 @@ #include "TagItem.hxx" #include "util/Cast.hxx" #include "util/VarSize.hxx" +#include "util/StringView.hxx" #include <assert.h> #include <string.h> @@ -37,39 +38,37 @@ struct TagPoolSlot { TagItem item; TagPoolSlot(TagPoolSlot *_next, TagType type, - const char *value, size_t length) + StringView value) :next(_next), ref(1) { item.type = type; - memcpy(item.value, value, length); - item.value[length] = 0; + memcpy(item.value, value.data, value.size); + item.value[value.size] = 0; } static TagPoolSlot *Create(TagPoolSlot *_next, TagType type, - const char *value, size_t length); + StringView value); } gcc_packed; TagPoolSlot * TagPoolSlot::Create(TagPoolSlot *_next, TagType type, - const char *value, size_t length) + StringView value) { TagPoolSlot *dummy; return NewVarSize<TagPoolSlot>(sizeof(dummy->item.value), - length + 1, + value.size + 1, _next, type, - value, length); + value); } static TagPoolSlot *slots[NUM_SLOTS]; static inline unsigned -calc_hash_n(TagType type, const char *p, size_t length) +calc_hash(TagType type, StringView p) { unsigned hash = 5381; - assert(p != nullptr); - - while (length-- > 0) - hash = (hash << 5) + hash + *p++; + for (auto ch : p) + hash = (hash << 5) + hash + ch; return hash ^ type; } @@ -87,7 +86,7 @@ calc_hash(TagType type, const char *p) return hash ^ type; } -#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#if CLANG_OR_GCC_VERSION(4,7) constexpr #endif static inline TagPoolSlot * @@ -97,9 +96,9 @@ tag_item_to_slot(TagItem *item) } static inline TagPoolSlot ** -tag_value_slot_p(TagType type, const char *value, size_t length) +tag_value_slot_p(TagType type, StringView value) { - return &slots[calc_hash_n(type, value, length) % NUM_SLOTS]; + return &slots[calc_hash(type, value) % NUM_SLOTS]; } static inline TagPoolSlot ** @@ -109,13 +108,12 @@ tag_value_slot_p(TagType type, const char *value) } TagItem * -tag_pool_get_item(TagType type, const char *value, size_t length) +tag_pool_get_item(TagType type, StringView value) { - auto slot_p = tag_value_slot_p(type, value, length); + auto slot_p = tag_value_slot_p(type, value); for (auto slot = *slot_p; slot != nullptr; slot = slot->next) { if (slot->item.type == type && - length == strlen(slot->item.value) && - memcmp(value, slot->item.value, length) == 0 && + value.Equals(slot->item.value) && slot->ref < 0xff) { assert(slot->ref > 0); ++slot->ref; @@ -123,7 +121,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length) } } - auto slot = TagPoolSlot::Create(*slot_p, type, value, length); + auto slot = TagPoolSlot::Create(*slot_p, type, value); *slot_p = slot; return &slot->item; } @@ -141,11 +139,9 @@ tag_pool_dup_item(TagItem *item) } else { /* the reference counter overflows above 0xff; duplicate the item, and start with 1 */ - size_t length = strlen(item->value); - auto slot_p = tag_value_slot_p(item->type, - item->value, length); + auto slot_p = tag_value_slot_p(item->type, item->value); slot = TagPoolSlot::Create(*slot_p, item->type, - item->value, strlen(item->value)); + item->value); *slot_p = slot; return &slot->item; } diff --git a/src/tag/TagPool.hxx b/src/tag/TagPool.hxx index 990ee87bd..7b4b5dadf 100644 --- a/src/tag/TagPool.hxx +++ b/src/tag/TagPool.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,9 +26,10 @@ extern Mutex tag_pool_lock; struct TagItem; +struct StringView; TagItem * -tag_pool_get_item(TagType type, const char *value, size_t length); +tag_pool_get_item(TagType type, StringView value); TagItem * tag_pool_dup_item(TagItem *item); diff --git a/src/tag/TagRva2.cxx b/src/tag/TagRva2.cxx index bbb6d11e6..8e22cd693 100644 --- a/src/tag/TagRva2.cxx +++ b/src/tag/TagRva2.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,42 +26,42 @@ #include <stdint.h> #include <string.h> -enum rva2_channel { - CHANNEL_OTHER = 0x00, - CHANNEL_MASTER_VOLUME = 0x01, - CHANNEL_FRONT_RIGHT = 0x02, - CHANNEL_FRONT_LEFT = 0x03, - CHANNEL_BACK_RIGHT = 0x04, - CHANNEL_BACK_LEFT = 0x05, - CHANNEL_FRONT_CENTRE = 0x06, - CHANNEL_BACK_CENTRE = 0x07, - CHANNEL_SUBWOOFER = 0x08 +enum class Rva2Channel : uint8_t { + OTHER = 0x00, + MASTER_VOLUME = 0x01, + FRONT_RIGHT = 0x02, + FRONT_LEFT = 0x03, + BACK_RIGHT = 0x04, + BACK_LEFT = 0x05, + FRONT_CENTRE = 0x06, + BACK_CENTRE = 0x07, + SUBWOOFER = 0x08 }; -struct rva2_data { - uint8_t type; +struct Rva2Data { + Rva2Channel type; uint8_t volume_adjustment[2]; uint8_t peak_bits; }; static inline id3_length_t -rva2_peak_bytes(const struct rva2_data *data) +rva2_peak_bytes(const Rva2Data &data) { - return (data->peak_bits + 7) / 8; + return (data.peak_bits + 7) / 8; } static inline int -rva2_fixed_volume_adjustment(const struct rva2_data *data) +rva2_fixed_volume_adjustment(const Rva2Data &data) { signed int voladj_fixed; - voladj_fixed = (data->volume_adjustment[0] << 8) | - data->volume_adjustment[1]; + voladj_fixed = (data.volume_adjustment[0] << 8) | + data.volume_adjustment[1]; voladj_fixed |= -(voladj_fixed & 0x8000); return voladj_fixed; } static inline float -rva2_float_volume_adjustment(const struct rva2_data *data) +rva2_float_volume_adjustment(const Rva2Data &data) { /* * "The volume adjustment is encoded as a fixed point decibel @@ -74,9 +74,9 @@ rva2_float_volume_adjustment(const struct rva2_data *data) static inline bool rva2_apply_data(ReplayGainInfo &rgi, - const struct rva2_data *data, const id3_latin1_t *id) + const Rva2Data &data, const id3_latin1_t *id) { - if (data->type != CHANNEL_MASTER_VOLUME) + if (data.type != Rva2Channel::MASTER_VOLUME) return false; float volume_adjustment = rva2_float_volume_adjustment(data); @@ -117,7 +117,7 @@ rva2_apply_frame(ReplayGainInfo &replay_gain_info, */ while (length >= 4) { - const struct rva2_data *d = (const struct rva2_data *)data; + const Rva2Data &d = *(const Rva2Data *)data; unsigned int peak_bytes = rva2_peak_bytes(d); if (4 + peak_bytes > length) break; diff --git a/src/tag/TagRva2.hxx b/src/tag/TagRva2.hxx index df559f472..615d5a282 100644 --- a/src/tag/TagRva2.hxx +++ b/src/tag/TagRva2.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -26,7 +26,7 @@ struct id3_tag; struct ReplayGainInfo; /** - * Parse the RVA2 tag, and fill the #replay_gain_info struct. This is + * Parse the RVA2 tag, and fill the #ReplayGainInfo struct. This is * used by decoder plugins with ID3 support. * * @return true on success diff --git a/src/tag/TagString.cxx b/src/tag/TagString.cxx index 4f07cd62a..d30a07a27 100644 --- a/src/tag/TagString.cxx +++ b/src/tag/TagString.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -21,6 +21,7 @@ #include "TagString.hxx" #include "util/Alloc.hxx" #include "util/WritableBuffer.hxx" +#include "util/StringView.hxx" #include "util/UTF8.hxx" #include <assert.h> @@ -54,14 +55,14 @@ FindInvalidUTF8(const char *p, const char *const end) * Replace invalid sequences with the question mark. */ static WritableBuffer<char> -patch_utf8(const char *src, size_t length, const char *_invalid) +patch_utf8(StringView src, const char *_invalid) { /* duplicate the string, and replace invalid bytes in that buffer */ - char *dest = (char *)xmemdup(src, length); - char *const end = dest + length; + char *dest = (char *)xmemdup(src.data, src.size); + char *const end = dest + src.size; - char *invalid = dest + (_invalid - src); + char *invalid = dest + (_invalid - src.data); do { *invalid = '?'; @@ -69,19 +70,19 @@ patch_utf8(const char *src, size_t length, const char *_invalid) invalid = const_cast<char *>(__invalid); } while (invalid != nullptr); - return { dest, length }; + return { dest, src.size }; } static WritableBuffer<char> -fix_utf8(const char *str, size_t length) +fix_utf8(StringView p) { /* check if the string is already valid UTF-8 */ - const char *invalid = FindInvalidUTF8(str, str + length); + const char *invalid = FindInvalidUTF8(p.begin(), p.end()); if (invalid == nullptr) return nullptr; /* no, broken - patch invalid sequences */ - return patch_utf8(str, length, invalid); + return patch_utf8(p, invalid); } static bool @@ -91,11 +92,11 @@ char_is_non_printable(unsigned char ch) } static const char * -find_non_printable(const char *p, size_t length) +find_non_printable(StringView p) { - for (size_t i = 0; i < length; ++i) - if (char_is_non_printable(p[i])) - return p + i; + for (const char &ch : p) + if (char_is_non_printable(ch)) + return &ch; return nullptr; } @@ -105,31 +106,29 @@ find_non_printable(const char *p, size_t length) * Returns nullptr if nothing needs to be cleared. */ static WritableBuffer<char> -clear_non_printable(const char *p, size_t length) +clear_non_printable(StringView src) { - const char *first = find_non_printable(p, length); + const char *first = find_non_printable(src); if (first == nullptr) return nullptr; - char *dest = (char *)xmemdup(p, length); + char *dest = (char *)xmemdup(src.data, src.size); - for (size_t i = first - p; i < length; ++i) + for (size_t i = first - src.data; i < src.size; ++i) if (char_is_non_printable(dest[i])) dest[i] = ' '; - return { dest, length }; + return { dest, src.size }; } WritableBuffer<char> -FixTagString(const char *p, size_t length) +FixTagString(StringView p) { - auto utf8 = fix_utf8(p, length); - if (!utf8.IsNull()) { - p = utf8.data; - length = utf8.size; - } + auto utf8 = fix_utf8(p); + if (!utf8.IsNull()) + p = {utf8.data, utf8.size}; - WritableBuffer<char> cleared = clear_non_printable(p, length); + WritableBuffer<char> cleared = clear_non_printable(p); if (cleared.IsNull()) cleared = utf8; else diff --git a/src/tag/TagString.hxx b/src/tag/TagString.hxx index eccc2aa47..53fbc7abd 100644 --- a/src/tag/TagString.hxx +++ b/src/tag/TagString.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,10 +25,11 @@ #include <stddef.h> +struct StringView; template<typename T> struct WritableBuffer; gcc_nonnull_all WritableBuffer<char> -FixTagString(const char *p, size_t length); +FixTagString(StringView p); #endif diff --git a/src/tag/TagTable.cxx b/src/tag/TagTable.cxx index c6e1cff54..e2a22b642 100644 --- a/src/tag/TagTable.cxx +++ b/src/tag/TagTable.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagTable.hxx b/src/tag/TagTable.hxx index 095b4cbff..0d72cba99 100644 --- a/src/tag/TagTable.hxx +++ b/src/tag/TagTable.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/TagType.h b/src/tag/TagType.h index 0aa6b4a51..6f68d53d0 100644 --- a/src/tag/TagType.h +++ b/src/tag/TagType.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/VorbisComment.cxx b/src/tag/VorbisComment.cxx index 2dfc058d8..2c45470d4 100644 --- a/src/tag/VorbisComment.cxx +++ b/src/tag/VorbisComment.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/tag/VorbisComment.hxx b/src/tag/VorbisComment.hxx index 1dd3371c8..e48e9b7a9 100644 --- a/src/tag/VorbisComment.hxx +++ b/src/tag/VorbisComment.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/thread/Cond.hxx b/src/thread/Cond.hxx index a05d1c67d..ed016d212 100644 --- a/src/thread/Cond.hxx +++ b/src/thread/Cond.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 Max Kellermann <max@duempel.org> + * Copyright (C) 2009-2015 Max Kellermann <max@duempel.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,8 +32,6 @@ #ifdef WIN32 -/* mingw-w64 4.6.3 lacks a std::cond implementation */ - #include "WindowsCond.hxx" class Cond : public WindowsCond {}; diff --git a/src/thread/Id.hxx b/src/thread/Id.hxx index 11be0a56b..60006e31a 100644 --- a/src/thread/Id.hxx +++ b/src/thread/Id.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/thread/Mutex.hxx b/src/thread/Mutex.hxx index c17538549..bc4deebdd 100644 --- a/src/thread/Mutex.hxx +++ b/src/thread/Mutex.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014 Max Kellermann <max@duempel.org> + * Copyright (C) 2009-2015 Max Kellermann <max@duempel.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,8 +32,6 @@ #ifdef WIN32 -/* mingw-w64 4.6.3 lacks a std::mutex implementation */ - #include "CriticalSection.hxx" class Mutex : public CriticalSection {}; diff --git a/src/thread/Name.hxx b/src/thread/Name.hxx index a99208dab..999cebf73 100644 --- a/src/thread/Name.hxx +++ b/src/thread/Name.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/thread/PosixCond.hxx b/src/thread/PosixCond.hxx index b3fe204e1..73dbe0218 100644 --- a/src/thread/PosixCond.hxx +++ b/src/thread/PosixCond.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 Max Kellermann <max@duempel.org> + * Copyright (C) 2009-2015 Max Kellermann <max@duempel.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,9 +41,13 @@ class PosixCond { pthread_cond_t cond; public: -#if defined(__NetBSD__) || defined(__BIONIC__) - /* NetBSD's PTHREAD_COND_INITIALIZER is not compatible with - "constexpr" */ +#ifdef __GLIBC__ + /* optimized constexpr constructor for pthread implementations + that support it */ + constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {} +#else + /* slow fallback for pthread implementations that are not + compatible with "constexpr" */ PosixCond() { pthread_cond_init(&cond, nullptr); } @@ -51,10 +55,6 @@ public: ~PosixCond() { pthread_cond_destroy(&cond); } -#else - /* optimized constexpr constructor for sane POSIX - implementations */ - constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {} #endif PosixCond(const PosixCond &other) = delete; diff --git a/src/thread/PosixMutex.hxx b/src/thread/PosixMutex.hxx index 5805158d5..e0fd614c8 100644 --- a/src/thread/PosixMutex.hxx +++ b/src/thread/PosixMutex.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2013 Max Kellermann <max@duempel.org> + * Copyright (C) 2009-2015 Max Kellermann <max@duempel.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -41,9 +41,13 @@ class PosixMutex { pthread_mutex_t mutex; public: -#if defined(__NetBSD__) || defined(__BIONIC__) - /* NetBSD's PTHREAD_MUTEX_INITIALIZER is not compatible with - "constexpr" */ +#ifdef __GLIBC__ + /* optimized constexpr constructor for pthread implementations + that support it */ + constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {} +#else + /* slow fallback for pthread implementations that are not + compatible with "constexpr" */ PosixMutex() { pthread_mutex_init(&mutex, nullptr); } @@ -51,10 +55,6 @@ public: ~PosixMutex() { pthread_mutex_destroy(&mutex); } -#else - /* optimized constexpr constructor for sane POSIX - implementations */ - constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {} #endif PosixMutex(const PosixMutex &other) = delete; diff --git a/src/thread/Slack.hxx b/src/thread/Slack.hxx index 66b2254a4..3f9c71d52 100644 --- a/src/thread/Slack.hxx +++ b/src/thread/Slack.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/thread/Thread.cxx b/src/thread/Thread.cxx index 2932d478f..78675d84e 100644 --- a/src/thread/Thread.cxx +++ b/src/thread/Thread.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/thread/Thread.hxx b/src/thread/Thread.hxx index 976ff5625..668f5e9f7 100644 --- a/src/thread/Thread.hxx +++ b/src/thread/Thread.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/unix/Daemon.cxx b/src/unix/Daemon.cxx index d283108ed..9aa16a078 100644 --- a/src/unix/Daemon.cxx +++ b/src/unix/Daemon.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -36,6 +36,10 @@ #include <grp.h> #endif +#ifndef WCOREDUMP +#define WCOREDUMP(v) 0 +#endif + #ifndef WIN32 /** the Unix user name which MPD runs as */ @@ -62,28 +66,17 @@ static int detach_fd = -1; void daemonize_kill(void) { - FILE *fp; - int pid, ret; - if (pidfile.IsNull()) FatalError("no pid_file specified in the config file"); - fp = FOpen(pidfile, "r"); - if (fp == nullptr) { - const std::string utf8 = pidfile.ToUTF8(); - FormatFatalSystemError("Unable to open pid file \"%s\"", - utf8.c_str()); - } - - if (fscanf(fp, "%i", &pid) != 1) { + const pid_t pid = ReadPidFile(pidfile); + if (pid < 0) { const std::string utf8 = pidfile.ToUTF8(); FormatFatalError("unable to read the pid from file \"%s\"", utf8.c_str()); } - fclose(fp); - ret = kill(pid, SIGTERM); - if (ret < 0) + if (kill(pid, SIGTERM) < 0) FormatFatalSystemError("unable to kill process %i", int(pid)); diff --git a/src/unix/Daemon.hxx b/src/unix/Daemon.hxx index fe5681511..5937705ce 100644 --- a/src/unix/Daemon.hxx +++ b/src/unix/Daemon.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -33,10 +33,10 @@ daemonize_init(const char *user, const char *group, AllocatedPath &&pidfile) #ifndef WIN32 void -daemonize_finish(void); +daemonize_finish(); #else static inline void -daemonize_finish(void) +daemonize_finish() { /* nop */ } #endif @@ -46,11 +46,11 @@ daemonize_finish(void) */ #ifndef WIN32 void -daemonize_kill(void); +daemonize_kill(); #else #include "system/FatalError.hxx" static inline void -daemonize_kill(void) +daemonize_kill() { FatalError("--kill is not available on WIN32"); } @@ -61,10 +61,10 @@ daemonize_kill(void) */ #ifndef WIN32 void -daemonize_close_stdin(void); +daemonize_close_stdin(); #else static inline void -daemonize_close_stdin(void) {} +daemonize_close_stdin() {} #endif /** @@ -72,10 +72,10 @@ daemonize_close_stdin(void) {} */ #ifndef WIN32 void -daemonize_set_user(void); +daemonize_set_user(); #else static inline void -daemonize_set_user(void) +daemonize_set_user() { /* nop */ } #endif diff --git a/src/unix/PidFile.hxx b/src/unix/PidFile.hxx index a242c7810..cbb681554 100644 --- a/src/unix/PidFile.hxx +++ b/src/unix/PidFile.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -25,61 +25,90 @@ #include "Log.hxx" #include <assert.h> -#include <stdio.h> -#include <sys/types.h> +#include <string.h> #include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> class PidFile { - FILE *file; + int fd; public: - PidFile(const AllocatedPath &path):file(nullptr) { + PidFile(const AllocatedPath &path):fd(-1) { if (path.IsNull()) return; - file = FOpen(path, "w"); - if (file == nullptr) { + fd = OpenFile(path, O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd < 0) { const std::string utf8 = path.ToUTF8(); FormatFatalSystemError("Failed to create pid file \"%s\"", - path.c_str()); + utf8.c_str()); } } PidFile(const PidFile &) = delete; void Close() { - if (file == nullptr) + if (fd < 0) return; - fclose(file); + close(fd); } void Delete(const AllocatedPath &path) { - if (file == nullptr) { + if (fd < 0) { assert(path.IsNull()); return; } assert(!path.IsNull()); - fclose(file); + close(fd); RemoveFile(path); } void Write(pid_t pid) { - if (file == nullptr) + if (fd < 0) return; - fprintf(file, "%lu\n", (unsigned long)pid); - fclose(file); + char buffer[64]; + sprintf(buffer, "%lu\n", (unsigned long)pid); + + write(fd, buffer, strlen(buffer)); + close(fd); } void Write() { - if (file == nullptr) + if (fd < 0) return; Write(getpid()); } }; +gcc_pure +static inline pid_t +ReadPidFile(Path path) +{ + int fd = OpenFile(path, O_RDONLY, 0); + if (fd < 0) + return -1; + + pid_t pid = -1; + + char buffer[32]; + auto nbytes = read(fd, buffer, sizeof(buffer) - 1); + if (nbytes > 0) { + buffer[nbytes] = 0; + + char *endptr; + auto value = strtoul(buffer, &endptr, 10); + if (endptr > buffer) + pid = value; + } + + close(fd); + return pid; +} + #endif diff --git a/src/unix/SignalHandlers.cxx b/src/unix/SignalHandlers.cxx index 4aef4fa71..47085b4fd 100644 --- a/src/unix/SignalHandlers.cxx +++ b/src/unix/SignalHandlers.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/unix/SignalHandlers.hxx b/src/unix/SignalHandlers.hxx index 551b373c1..573db7511 100644 --- a/src/unix/SignalHandlers.hxx +++ b/src/unix/SignalHandlers.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/ASCII.hxx b/src/util/ASCII.hxx index 9f7147338..d9a2198e1 100644 --- a/src/util/ASCII.hxx +++ b/src/util/ASCII.hxx @@ -27,8 +27,8 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MPD_ASCII_HXX -#define MPD_ASCII_HXX +#ifndef ASCII_HXX +#define ASCII_HXX #include "Compiler.h" diff --git a/src/util/Alloc.cxx b/src/util/Alloc.cxx index 006e09701..c2676ca3d 100644 --- a/src/util/Alloc.cxx +++ b/src/util/Alloc.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -75,3 +75,87 @@ xstrndup(const char *s, size_t n) return p; } + +#if CLANG_OR_GCC_VERSION(4,7) + +template<typename... Args> +static inline size_t +FillLengths(size_t *lengths, const char *a, Args&&... args) +{ + return FillLengths(lengths, a) + FillLengths(lengths + 1, args...); +} + +template<> +inline size_t +FillLengths(size_t *lengths, const char *a) +{ + return *lengths = strlen(a); +} + +template<typename... Args> +static inline void +StringCat(char *p, const size_t *lengths, const char *a, Args&&... args) +{ + StringCat(p, lengths, a); + StringCat(p + *lengths, lengths + 1, args...); +} + +template<> +inline void +StringCat(char *p, const size_t *lengths, const char *a) +{ + memcpy(p, a, *lengths); +} + +#endif + +template<typename... Args> +gcc_malloc gcc_nonnull_all +static inline char * +t_xstrcatdup(Args&&... args) +{ +#if CLANG_OR_GCC_VERSION(4,7) + constexpr size_t n = sizeof...(args); + + size_t lengths[n]; + const size_t total = FillLengths(lengths, args...); + + char *p = (char *)xalloc(total + 1); + StringCat(p, lengths, args...); + p[total] = 0; + return p; +#else + /* fallback implementation for gcc 4.6, because that old + compiler is too buggy to compile the above template + functions */ + const char *const argv[] = { args... }; + + size_t total = 0; + for (auto i : argv) + total += strlen(i); + + char *p = (char *)xalloc(total + 1), *q = p; + for (auto i : argv) + q = stpcpy(q, i); + + return p; +#endif +} + +char * +xstrcatdup(const char *a, const char *b) +{ + return t_xstrcatdup(a, b); +} + +char * +xstrcatdup(const char *a, const char *b, const char *c) +{ + return t_xstrcatdup(a, b, c); +} + +char * +xstrcatdup(const char *a, const char *b, const char *c, const char *d) +{ + return t_xstrcatdup(a, b, c, d); +} diff --git a/src/util/Alloc.hxx b/src/util/Alloc.hxx index 15c123b7a..9e1007e69 100644 --- a/src/util/Alloc.hxx +++ b/src/util/Alloc.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -64,4 +64,23 @@ gcc_malloc gcc_nonnull_all char * xstrndup(const char *s, size_t n); +/** + * Concatenate two strings, returning a new allocation. Use free() to + * free it. + * + * This function never fails; in out-of-memory situations, it aborts + * the process. + */ +gcc_malloc gcc_nonnull_all +char * +xstrcatdup(const char *a, const char *b); + +gcc_malloc gcc_nonnull_all +char * +xstrcatdup(const char *a, const char *b, const char *c); + +gcc_malloc gcc_nonnull_all +char * +xstrcatdup(const char *a, const char *b, const char *c, const char *d); + #endif diff --git a/src/util/AllocatedString.cxx b/src/util/AllocatedString.cxx new file mode 100644 index 000000000..486462fa4 --- /dev/null +++ b/src/util/AllocatedString.cxx @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "AllocatedString.hxx" +#include "StringAPI.hxx" + +template<> +AllocatedString<char> +AllocatedString<char>::Duplicate(const_pointer src) +{ + return Duplicate(src, StringLength(src)); +} diff --git a/src/util/AllocatedString.hxx b/src/util/AllocatedString.hxx new file mode 100644 index 000000000..01eefac8a --- /dev/null +++ b/src/util/AllocatedString.hxx @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ALLOCATED_STRING_HXX +#define ALLOCATED_STRING_HXX + +#include "StringPointer.hxx" + +#include <utility> +#include <algorithm> + +/** + * A string pointer whose memory is managed by this class. + * + * Unlike std::string, this object can hold a "nullptr" special value. + */ +template<typename T=char> +class AllocatedString { +public: + typedef typename StringPointer<T>::value_type value_type; + typedef typename StringPointer<T>::pointer pointer; + typedef typename StringPointer<T>::const_pointer const_pointer; + + static constexpr value_type SENTINEL = '\0'; + +private: + pointer value; + + explicit AllocatedString(pointer _value) + :value(_value) {} + +public: + AllocatedString(std::nullptr_t n):value(n) {} + + AllocatedString(AllocatedString &&src) + :value(src.Steal()) {} + + ~AllocatedString() { + delete[] value; + } + + static AllocatedString Donate(pointer value) { + return AllocatedString(value); + } + + static AllocatedString Null() { + return nullptr; + } + + static AllocatedString Empty() { + auto p = new value_type[1]; + p[0] = SENTINEL; + return Donate(p); + } + + static AllocatedString Duplicate(const_pointer src); + + static AllocatedString Duplicate(const_pointer begin, + const_pointer end) { + auto p = new value_type[end - begin + 1]; + *std::copy(begin, end, p) = SENTINEL; + return Donate(p); + } + + static AllocatedString Duplicate(const_pointer begin, + size_t length) { + auto p = new value_type[length + 1]; + *std::copy_n(begin, length, p) = SENTINEL; + return Donate(p); + } + + AllocatedString &operator=(AllocatedString &&src) { + std::swap(value, src.value); + return *this; + } + + constexpr bool IsNull() const { + return value == nullptr; + } + + constexpr const_pointer c_str() const { + return value; + } + + bool empty() const { + return *value == SENTINEL; + } + + pointer Steal() { + pointer result = value; + value = nullptr; + return result; + } + + AllocatedString Clone() const { + return Duplicate(c_str()); + } +}; + +#endif diff --git a/src/util/ByteReverse.cxx b/src/util/ByteReverse.cxx index 5cc8692a7..c0c2946ca 100644 --- a/src/util/ByteReverse.cxx +++ b/src/util/ByteReverse.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/ByteReverse.hxx b/src/util/ByteReverse.hxx index 0c060c0cb..55d6a64db 100644 --- a/src/util/ByteReverse.hxx +++ b/src/util/ByteReverse.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/Cast.hxx b/src/util/Cast.hxx index 887137da4..647171970 100644 --- a/src/util/Cast.hxx +++ b/src/util/Cast.hxx @@ -84,7 +84,7 @@ ContainerAttributeOffset(const A C::*p) * Cast the given pointer to a struct member to its parent structure. */ template<class C, class A> -#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#if CLANG_OR_GCC_VERSION(4,7) constexpr #endif static inline C & @@ -97,7 +97,7 @@ ContainerCast(A &a, A C::*member) * Cast the given pointer to a struct member to its parent structure. */ template<class C, class A> -#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#if CLANG_OR_GCC_VERSION(4,7) constexpr #endif static inline const C & diff --git a/src/util/CharUtil.hxx b/src/util/CharUtil.hxx index 84a88a94e..efd40896a 100644 --- a/src/util/CharUtil.hxx +++ b/src/util/CharUtil.hxx @@ -128,7 +128,7 @@ ToUpperASCII(char ch) /** * Convert the specified ASCII character (0x00..0x7f) to lower case. - * Unlike toupper(), it ignores the system locale. + * Unlike tolower(), it ignores the system locale. */ constexpr static inline char diff --git a/src/util/ConstBuffer.hxx b/src/util/ConstBuffer.hxx index 4d0a49e98..b98a8b543 100644 --- a/src/util/ConstBuffer.hxx +++ b/src/util/ConstBuffer.hxx @@ -246,6 +246,20 @@ struct ConstBuffer { data += n; size -= n; } + + /** + * Move the front pointer to the given address, and adjust the + * size attribute to retain the old end address. + */ + void MoveFront(pointer_type new_data) { +#ifndef NDEBUG + assert(IsNull() == (new_data == nullptr)); + assert(new_data <= end()); +#endif + + size = end() - new_data; + data = new_data; + } }; #endif diff --git a/src/util/DeleteDisposer.hxx b/src/util/DeleteDisposer.hxx new file mode 100644 index 000000000..dd91e1299 --- /dev/null +++ b/src/util/DeleteDisposer.hxx @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DELETE_DISPOSER_HXX +#define DELETE_DISPOSER_HXX + +/** + * A disposer for boost::intrusive that invokes the "delete" operator + * on the given pointer. + */ +struct DeleteDisposer { + template<typename T> + void operator()(T *t) { + delete t; + } +}; + +#endif diff --git a/src/util/DivideString.cxx b/src/util/DivideString.cxx new file mode 100644 index 000000000..8e3d5a1b8 --- /dev/null +++ b/src/util/DivideString.cxx @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2003-2015 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 "DivideString.hxx" +#include "StringUtil.hxx" + +#include <string.h> + +DivideString::DivideString(const char *s, char separator, bool strip) + :first(nullptr) +{ + const char *x = strchr(s, separator); + if (x == nullptr) + return; + + size_t length = x - s; + second = x + 1; + + if (strip) + second = StripLeft(second); + + if (strip) { + const char *end = s + length; + s = StripLeft(s); + end = StripRight(s, end); + length = end - s; + } + + first = new char[length + 1]; + memcpy(first, s, length); + first[length] = 0; +} diff --git a/src/util/DivideString.hxx b/src/util/DivideString.hxx new file mode 100644 index 000000000..d98b512a6 --- /dev/null +++ b/src/util/DivideString.hxx @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2003-2015 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_DIVIDE_STRING_HXX +#define MPD_DIVIDE_STRING_HXX + +#include "Compiler.h" + +#include <assert.h> + +/** + * Split a given constant string at a separator character. Duplicates + * the first part to be able to null-terminate it. + */ +class DivideString { + char *first; + const char *second; + +public: + /** + * @param strip strip the first part and left-strip the second + * part? + */ + DivideString(const char *s, char separator, bool strip=false); + + ~DivideString() { + delete[] first; + } + + /** + * Was the separator found? + */ + bool IsDefined() const { + return first != nullptr; + } + + /** + * Is the first part empty? + */ + bool IsEmpty() const { + assert(IsDefined()); + + return *first == 0; + } + + const char *GetFirst() const { + assert(IsDefined()); + + return first; + } + + const char *GetSecond() const { + assert(IsDefined()); + + return second; + } +}; + +#endif diff --git a/src/util/DynamicFifoBuffer.hxx b/src/util/DynamicFifoBuffer.hxx index c1e5d1b94..5a9056edb 100644 --- a/src/util/DynamicFifoBuffer.hxx +++ b/src/util/DynamicFifoBuffer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2013 Max Kellermann <max@duempel.org> + * Copyright (C) 2003-2015 Max Kellermann <max@duempel.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,8 +27,8 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef FIFO_BUFFER_HPP -#define FIFO_BUFFER_HPP +#ifndef DYNAMIC_FIFO_BUFFER_HXX +#define DYNAMIC_FIFO_BUFFER_HXX #include "ForeignFifoBuffer.hxx" diff --git a/src/util/Error.cxx b/src/util/Error.cxx index 92b2cc5d0..67a1b03fd 100644 --- a/src/util/Error.cxx +++ b/src/util/Error.cxx @@ -32,7 +32,7 @@ #include "Domain.hxx" #ifdef WIN32 -#include <glib.h> +#include <windows.h> #endif #include <errno.h> @@ -135,7 +135,11 @@ Error::FormatErrno(const char *fmt, ...) void Error::SetLastError(DWORD _code, const char *prefix) { - const char *msg = g_win32_error_message(_code); + char msg[256]; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, _code, 0, msg, sizeof(msg), nullptr); + Format(win32_domain, int(_code), "%s: %s", prefix, msg); } diff --git a/src/util/FormatString.cxx b/src/util/FormatString.cxx index d222a505c..5ada067cb 100644 --- a/src/util/FormatString.cxx +++ b/src/util/FormatString.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/FormatString.hxx b/src/util/FormatString.hxx index dc1ac3c67..b0f8dd7f9 100644 --- a/src/util/FormatString.hxx +++ b/src/util/FormatString.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/LazyRandomEngine.cxx b/src/util/LazyRandomEngine.cxx index b0aac913c..abd83da8c 100644 --- a/src/util/LazyRandomEngine.cxx +++ b/src/util/LazyRandomEngine.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/LazyRandomEngine.hxx b/src/util/LazyRandomEngine.hxx index 4156b3bb1..7b9b1c655 100644 --- a/src/util/LazyRandomEngine.hxx +++ b/src/util/LazyRandomEngine.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/Manual.hxx b/src/util/Manual.hxx index 75cffac06..6ba932bdd 100644 --- a/src/util/Manual.hxx +++ b/src/util/Manual.hxx @@ -41,7 +41,7 @@ #include <assert.h> -#if defined(__clang__) || GCC_CHECK_VERSION(4,7) +#if CLANG_OR_GCC_VERSION(4,7) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif @@ -54,12 +54,7 @@ */ template<class T> class Manual { -#if GCC_OLDER_THAN(4,8) - /* no alignas() on gcc < 4.8: apply worst-case fallback */ - __attribute__((aligned(8))) -#else - alignas(T) -#endif + gcc_alignas(T, 8) char data[sizeof(T)]; #ifndef NDEBUG @@ -89,32 +84,46 @@ public: void Destruct() { assert(initialized); - T *t = (T *)data; - t->T::~T(); + T &t = Get(); + t.T::~T(); #ifndef NDEBUG initialized = false; #endif } + T &Get() { + assert(initialized); + + void *p = static_cast<void *>(data); + return *static_cast<T *>(p); + } + + const T &Get() const { + assert(initialized); + + const void *p = static_cast<const void *>(data); + return *static_cast<const T *>(p); + } + operator T &() { - return *(T *)data; + return Get(); } operator const T &() const { - return *(const T *)data; + return Get(); } T *operator->() { - return (T *)data; + return &Get(); } const T *operator->() const { - return (T *)data; + return &Get(); } }; -#if defined(__clang__) || GCC_VERSION >= 40700 +#if CLANG_OR_GCC_VERSION(4,7) #pragma GCC diagnostic pop #endif diff --git a/src/util/OptionDef.hxx b/src/util/OptionDef.hxx index dd82154c4..29b7e268b 100644 --- a/src/util/OptionDef.hxx +++ b/src/util/OptionDef.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/OptionParser.cxx b/src/util/OptionParser.cxx index b10008527..45be084c9 100644 --- a/src/util/OptionParser.cxx +++ b/src/util/OptionParser.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/OptionParser.hxx b/src/util/OptionParser.hxx index b9d34adbb..c6c794a7d 100644 --- a/src/util/OptionParser.hxx +++ b/src/util/OptionParser.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/PeakBuffer.cxx b/src/util/PeakBuffer.cxx index e4624bbec..da3b275d8 100644 --- a/src/util/PeakBuffer.cxx +++ b/src/util/PeakBuffer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/PeakBuffer.hxx b/src/util/PeakBuffer.hxx index 702a3dee0..784b3cdbd 100644 --- a/src/util/PeakBuffer.hxx +++ b/src/util/PeakBuffer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/RefCount.hxx b/src/util/RefCount.hxx index 02ef8818c..c6cf2e41f 100644 --- a/src/util/RefCount.hxx +++ b/src/util/RefCount.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * Redistribution and use in source and binary forms, with or without diff --git a/src/util/SliceBuffer.hxx b/src/util/SliceBuffer.hxx index 63ca087ae..16c1cf744 100644 --- a/src/util/SliceBuffer.hxx +++ b/src/util/SliceBuffer.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/SplitString.cxx b/src/util/SplitString.cxx index 75e799279..9588312f8 100644 --- a/src/util/SplitString.cxx +++ b/src/util/SplitString.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,20 +18,42 @@ */ #include "SplitString.hxx" +#include "StringUtil.hxx" #include <string.h> -SplitString::SplitString(const char *s, char separator) - :first(nullptr) +std::forward_list<std::string> +SplitString(const char *s, char separator, bool strip) { - const char *x = strchr(s, separator); - if (x == nullptr) - return; + if (strip) + s = StripLeft(s); - size_t length = x - s; - second = x + 1; + std::forward_list<std::string> list; + if (*s == 0) + return list; - first = new char[length + 1]; - memcpy(first, s, length); - first[length] = 0; + auto i = list.before_begin(); + + while (true) { + const char *next = strchr(s, separator); + if (next == nullptr) + break; + + const char *end = next++; + if (strip) + end = StripRight(s, end); + + i = list.emplace_after(i, s, end); + + s = next; + if (strip) + s = StripLeft(s); + } + + const char *end = s + strlen(s); + if (strip) + end = StripRight(s, end); + + list.emplace_after(i, s, end); + return list; } diff --git a/src/util/SplitString.hxx b/src/util/SplitString.hxx index 96ffb21ec..545470c7a 100644 --- a/src/util/SplitString.hxx +++ b/src/util/SplitString.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -20,52 +20,20 @@ #ifndef MPD_SPLIT_STRING_HXX #define MPD_SPLIT_STRING_HXX -#include "Compiler.h" - -#include <assert.h> +#include <forward_list> +#include <string> /** - * Split a given constant string at a separator character. Duplicates - * the first part to be able to null-terminate it. + * Split a string at a certain separator character into sub strings + * and returns a list of these. + * + * Two consecutive separator characters result in an empty string in + * the list. + * + * An empty input string, as a special case, results in an empty list + * (and not a list with an empty string). */ -class SplitString { - char *first; - const char *second; - -public: - SplitString(const char *s, char separator); - - ~SplitString() { - delete[] first; - } - - /** - * Was the separator found? - */ - bool IsDefined() const { - return first != nullptr; - } - - /** - * Is the first part empty? - */ - bool IsEmpty() const { - assert(IsDefined()); - - return *first == 0; - } - - const char *GetFirst() const { - assert(IsDefined()); - - return first; - } - - const char *GetSecond() const { - assert(IsDefined()); - - return second; - } -}; +std::forward_list<std::string> +SplitString(const char *s, char separator, bool strip=true); #endif diff --git a/src/util/StringAPI.hxx b/src/util/StringAPI.hxx new file mode 100644 index 000000000..08087f5f8 --- /dev/null +++ b/src/util/StringAPI.hxx @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2010-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef STRING_API_HXX +#define STRING_API_HXX + +#include "Compiler.h" + +#include <string.h> + +#ifdef _UNICODE +#include "WStringAPI.hxx" +#endif + +gcc_pure gcc_nonnull_all +static inline size_t +StringLength(const char *p) +{ + return strlen(p); +} + +gcc_pure gcc_nonnull_all +static inline const char * +StringFind(const char *haystack, const char *needle) +{ + return strstr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline char * +StringFind(char *haystack, char needle, size_t size) +{ + return (char *)memchr(haystack, needle, size); +} + +gcc_pure gcc_nonnull_all +static inline const char * +StringFind(const char *haystack, char needle, size_t size) +{ + return (const char *)memchr(haystack, needle, size); +} + +gcc_pure gcc_nonnull_all +static inline const char * +StringFind(const char *haystack, char needle) +{ + return strchr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline char * +StringFind(char *haystack, char needle) +{ + return strchr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline const char * +StringFindLast(const char *haystack, char needle) +{ + return strrchr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline char * +StringFindLast(char *haystack, char needle) +{ + return strrchr(haystack, needle); +} + +gcc_nonnull_all +static inline void +UnsafeCopyString(char *dest, const char *src) +{ + strcpy(dest, src); +} + +gcc_nonnull_all +static inline char * +UnsafeCopyStringP(char *dest, const char *src) +{ +#if defined(WIN32) || defined(__BIONIC__) + /* emulate stpcpy() */ + UnsafeCopyString(dest, src); + return dest + StringLength(dest); +#else + return stpcpy(dest, src); +#endif +} + +/** + * Checks whether #a and #b are equal. + */ +gcc_pure gcc_nonnull_all +static inline bool +StringIsEqual(const char *a, const char *b) +{ + return strcmp(a, b) == 0; +} + +/** + * Checks whether #a and #b are equal. + */ +gcc_pure gcc_nonnull_all +static inline bool +StringIsEqual(const char *a, const char *b, size_t length) +{ + return strncmp(a, b, length) == 0; +} + +/** + * Copy the string to a new allocation. The return value must be + * freed with free(). + */ +gcc_malloc gcc_nonnull_all +static inline char * +DuplicateString(const char *p) +{ + return strdup(p); +} + +#endif diff --git a/src/util/StringPointer.hxx b/src/util/StringPointer.hxx new file mode 100644 index 000000000..4859d8265 --- /dev/null +++ b/src/util/StringPointer.hxx @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef STRING_POINTER_HXX +#define STRING_POINTER_HXX + +/** + * Simple OO wrapper for a const string pointer. + */ +template<typename T=char> +class StringPointer { +public: + typedef T value_type; + typedef T *pointer; + typedef const T *const_pointer; + +private: + const_pointer value; + +public: + StringPointer() = default; + constexpr StringPointer(const_pointer _value) + :value(_value) {} + + /** + * Check if this is a "nulled" instance. A "nulled" instance + * must not be used. + */ + constexpr bool IsNull() const { + return value == nullptr; + } + + constexpr const_pointer c_str() const { + return value; + } +}; + +#endif diff --git a/src/util/StringUtil.cxx b/src/util/StringUtil.cxx index bcade2b3b..b9c99eb4a 100644 --- a/src/util/StringUtil.cxx +++ b/src/util/StringUtil.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,6 +18,7 @@ */ #include "StringUtil.hxx" +#include "StringAPI.hxx" #include "CharUtil.hxx" #include "ASCII.hxx" @@ -26,6 +27,66 @@ #include <assert.h> #include <string.h> +bool +StringStartsWith(const char *haystack, const char *needle) +{ + const size_t length = strlen(needle); + return memcmp(haystack, needle, length) == 0; +} + +bool +StringEndsWith(const char *haystack, const char *needle) +{ + const size_t haystack_length = strlen(haystack); + const size_t needle_length = strlen(needle); + + return haystack_length >= needle_length && + memcmp(haystack + haystack_length - needle_length, + needle, needle_length) == 0; +} + +const char * +StringAfterPrefix(const char *string, const char *prefix) +{ +#if !CLANG_CHECK_VERSION(3,6) + /* disabled on clang due to -Wtautological-pointer-compare */ + assert(string != nullptr); + assert(prefix != nullptr); +#endif + + size_t prefix_length = strlen(prefix); + return StringIsEqual(string, prefix, prefix_length) + ? string + prefix_length + : nullptr; +} + +const char * +FindStringSuffix(const char *p, const char *suffix) +{ + const size_t p_length = strlen(p); + const size_t suffix_length = strlen(suffix); + + if (p_length < suffix_length) + return nullptr; + + const char *q = p + p_length - suffix_length; + return memcmp(q, suffix, suffix_length) == 0 + ? q + : nullptr; +} + +char * +CopyString(char *gcc_restrict dest, const char *gcc_restrict src, size_t size) +{ + size_t length = strlen(src); + if (length >= size) + length = size - 1; + + char *p = std::copy_n(src, length, dest); + *p = '\0'; + return p; +} + const char * StripLeft(const char *p) { @@ -79,36 +140,6 @@ Strip(char *p) } bool -StringStartsWith(const char *haystack, const char *needle) -{ - const size_t length = strlen(needle); - return memcmp(haystack, needle, length) == 0; -} - -bool -StringEndsWith(const char *haystack, const char *needle) -{ - const size_t haystack_length = strlen(haystack); - const size_t needle_length = strlen(needle); - - return haystack_length >= needle_length && - memcmp(haystack + haystack_length - needle_length, - needle, needle_length) == 0; -} - -char * -CopyString(char *gcc_restrict dest, const char *gcc_restrict src, size_t size) -{ - size_t length = strlen(src); - if (length >= size) - length = size - 1; - - char *p = std::copy(src, src + length, dest); - *p = '\0'; - return p; -} - -bool string_array_contains(const char *const* haystack, const char *needle) { assert(haystack != nullptr); @@ -120,3 +151,23 @@ string_array_contains(const char *const* haystack, const char *needle) return false; } + +void +ToUpperASCII(char *dest, const char *src, size_t size) +{ + assert(dest != nullptr); + assert(src != nullptr); + assert(size > 1); + + char *const end = dest + size - 1; + + do { + char ch = *src++; + if (ch == 0) + break; + + *dest++ = ToUpperASCII(ch); + } while (dest < end); + + *dest = 0; +} diff --git a/src/util/StringUtil.hxx b/src/util/StringUtil.hxx index 9beda5441..7e6dc4d61 100644 --- a/src/util/StringUtil.hxx +++ b/src/util/StringUtil.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -24,6 +24,47 @@ #include <stddef.h> +#ifdef _UNICODE +#include "WStringUtil.hxx" +#endif + +gcc_pure +bool +StringStartsWith(const char *haystack, const char *needle); + +gcc_pure +bool +StringEndsWith(const char *haystack, const char *needle); + +/** + * Returns the portion of the string after a prefix. If the string + * does not begin with the specified prefix, this function returns + * nullptr. + */ +gcc_pure gcc_nonnull_all +const char * +StringAfterPrefix(const char *string, const char *prefix); + +/** + * Check if the given string ends with the specified suffix. If yes, + * returns the position of the suffix, and nullptr otherwise. + */ +gcc_pure +const char * +FindStringSuffix(const char *p, const char *suffix); + +/** + * Copy a string. If the buffer is too small, then the string is + * truncated. This is a safer version of strncpy(). + * + * @param size the size of the destination buffer (including the null + * terminator) + * @return a pointer to the null terminator + */ +gcc_nonnull_all +char * +CopyString(char *dest, const char *src, size_t size); + /** * Returns a pointer to the first non-whitespace character in the * string, or to the end of the string. @@ -82,26 +123,6 @@ StripRight(char *p); char * Strip(char *p); -gcc_pure -bool -StringStartsWith(const char *haystack, const char *needle); - -gcc_pure -bool -StringEndsWith(const char *haystack, const char *needle); - -/** - * Copy a string. If the buffer is too small, then the string is - * truncated. This is a safer version of strncpy(). - * - * @param size the size of the destination buffer (including the null - * terminator) - * @return a pointer to the null terminator - */ -gcc_nonnull_all -char * -CopyString(char *dest, const char *src, size_t size); - /** * Checks whether a string array contains the specified string. * @@ -114,4 +135,12 @@ gcc_pure bool string_array_contains(const char *const* haystack, const char *needle); +/** + * Convert the specified ASCII string (0x00..0x7f) to upper case. + * + * @param size the destination buffer size + */ +void +ToUpperASCII(char *dest, const char *src, size_t size); + #endif diff --git a/src/util/StringView.cxx b/src/util/StringView.cxx new file mode 100644 index 000000000..904f92f65 --- /dev/null +++ b/src/util/StringView.cxx @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "StringView.hxx" +#include "CharUtil.hxx" + +void +StringView::StripLeft() +{ + while (!IsEmpty() && IsWhitespaceOrNull(front())) + pop_front(); +} + +void +StringView::StripRight() +{ + while (!IsEmpty() && IsWhitespaceOrNull(back())) + pop_back(); +} diff --git a/src/util/StringView.hxx b/src/util/StringView.hxx new file mode 100644 index 000000000..dd6c4dc77 --- /dev/null +++ b/src/util/StringView.hxx @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef STRING_VIEW_HXX +#define STRING_VIEW_HXX + +#include "ConstBuffer.hxx" + +#include <string.h> + +struct StringView : ConstBuffer<char> { + StringView() = default; + + constexpr StringView(pointer_type _data, size_type _size) + :ConstBuffer<char>(_data, _size) {} + + constexpr StringView(pointer_type _begin, pointer_type _end) + :ConstBuffer<char>(_begin, _end - _begin) {} + + StringView(pointer_type _data) + :ConstBuffer<char>(_data, + _data != nullptr ? strlen(_data) : 0) {} + + StringView(std::nullptr_t n) + :ConstBuffer<char>(n) {} + + static constexpr StringView Empty() { + return StringView("", size_t(0)); + } + + void SetEmpty() { + data = ""; + size = 0; + } + + gcc_pure + pointer_type Find(char ch) const { + return (pointer_type)memchr(data, ch, size); + } + + StringView &operator=(std::nullptr_t) { + data = nullptr; + size = 0; + return *this; + } + + StringView &operator=(pointer_type _data) { + data = _data; + size = _data != nullptr ? strlen(_data) : 0; + return *this; + } + + gcc_pure + bool StartsWith(StringView needle) const { + return size >= needle.size && + memcmp(data, needle.data, needle.size) == 0; + } + + gcc_pure + bool Equals(StringView other) const { + return size == other.size && + memcmp(data, other.data, size) == 0; + } + + template<size_t n> + bool EqualsLiteral(const char (&other)[n]) const { + return Equals({other, n - 1}); + } + + gcc_pure + bool EqualsIgnoreCase(StringView other) const { + return size == other.size && + strncasecmp(data, other.data, size) == 0; + } + + template<size_t n> + bool EqualsLiteralIgnoreCase(const char (&other)[n]) const { + return EqualsIgnoreCase({other, n - 1}); + } + + /** + * Skip all whitespace at the beginning. + */ + void StripLeft(); + + /** + * Skip all whitespace at the end. + */ + void StripRight(); +}; + +#endif diff --git a/src/util/Tokenizer.hxx b/src/util/Tokenizer.hxx index dc2646589..3f3448d52 100644 --- a/src/util/Tokenizer.hxx +++ b/src/util/Tokenizer.hxx @@ -71,7 +71,7 @@ public: /** * Reads the next unquoted word from the input string. * - * @param error_r if this function returns nullptr and **input_p!=0, it + * @param error if this function returns nullptr and **input_p!=0, it * provides an #Error object in this argument * @return a pointer to the null-terminated word, or nullptr * on error or end of line @@ -83,9 +83,7 @@ public: * escapes the following character. This function modifies the input * string. * - * @param input_p the input string; this function returns a pointer to - * the first non-whitespace character of the following token - * @param error_r if this function returns nullptr and **input_p!=0, it + * @param error if this function returns nullptr and **input_p!=0, it * provides an #Error object in this argument * @return a pointer to the null-terminated string, or nullptr on error * or end of line @@ -97,7 +95,7 @@ public: * input. This is a wrapper for NextUnquoted() and * NextString(). * - * @param error_r if this function returns nullptr and + * @param error if this function returns nullptr and * **input_p!=0, it provides an #Error object in * this argument * @return a pointer to the null-terminated string, or nullptr diff --git a/src/util/UriUtil.cxx b/src/util/UriUtil.cxx index 54d0ded77..0782304e3 100644 --- a/src/util/UriUtil.cxx +++ b/src/util/UriUtil.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/UriUtil.hxx b/src/util/UriUtil.hxx index d478d5b92..e8edfb5a0 100644 --- a/src/util/UriUtil.hxx +++ b/src/util/UriUtil.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/VarSize.hxx b/src/util/VarSize.hxx index 04f1bf580..4da115fba 100644 --- a/src/util/VarSize.hxx +++ b/src/util/VarSize.hxx @@ -42,7 +42,7 @@ * example when you want to store a variable-length string as the last * attribute without the overhead of a second allocation. * - * @param T a struct/class with a variable-size last attribute + * @tparam T a struct/class with a variable-size last attribute * @param declared_tail_size the declared size of the last element in * #T * @param real_tail_size the real required size of the last element in diff --git a/src/util/WStringAPI.hxx b/src/util/WStringAPI.hxx new file mode 100644 index 000000000..e020ecd7f --- /dev/null +++ b/src/util/WStringAPI.hxx @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010-2015 Max Kellermann <max@duempel.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WSTRING_API_HXX +#define WSTRING_API_HXX + +#include "Compiler.h" + +#include <wchar.h> + +gcc_pure gcc_nonnull_all +static inline size_t +StringLength(const wchar_t *p) +{ + return wcslen(p); +} + +gcc_pure gcc_nonnull_all +static inline const wchar_t * +StringFind(const wchar_t *haystack, const wchar_t *needle) +{ + return wcsstr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline const wchar_t * +StringFind(const wchar_t *haystack, wchar_t needle, size_t size) +{ + return wmemchr(haystack, needle, size); +} + +gcc_pure gcc_nonnull_all +static inline wchar_t * +StringFind(wchar_t *haystack, wchar_t needle, size_t size) +{ + return wmemchr(haystack, needle, size); +} + +gcc_pure gcc_nonnull_all +static inline const wchar_t * +StringFind(const wchar_t *haystack, wchar_t needle) +{ + return wcschr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline wchar_t * +StringFind(wchar_t *haystack, wchar_t needle) +{ + return wcschr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline const wchar_t * +StringFindLast(const wchar_t *haystack, wchar_t needle) +{ + return wcsrchr(haystack, needle); +} + +gcc_pure gcc_nonnull_all +static inline wchar_t * +StringFindLast(wchar_t *haystack, wchar_t needle) +{ + return wcsrchr(haystack, needle); +} + +gcc_nonnull_all +static inline void +UnsafeCopyString(wchar_t *dest, const wchar_t *src) +{ + wcscpy(dest, src); +} + +gcc_nonnull_all +static inline wchar_t * +UnsafeCopyStringP(wchar_t *dest, const wchar_t *src) +{ +#if defined(WIN32) || defined(__BIONIC__) + /* emulate wcpcpy() */ + UnsafeCopyString(dest, src); + return dest + StringLength(dest); +#else + return wcpcpy(dest, src); +#endif +} + +/** + * Checks whether str1 and str2 are equal. + * @param str1 String 1 + * @param str2 String 2 + * @return True if equal, False otherwise + */ +gcc_pure gcc_nonnull_all +static inline bool +StringIsEqual(const wchar_t *str1, const wchar_t *str2) +{ + return wcscmp(str1, str2) == 0; +} + +/** + * Checks whether #a and #b are equal. + */ +gcc_pure gcc_nonnull_all +static inline bool +StringIsEqual(const wchar_t *a, const wchar_t *b, size_t length) +{ + return wcsncmp(a, b, length) == 0; +} + +#ifndef __BIONIC__ + +gcc_malloc gcc_nonnull_all +static inline wchar_t * +DuplicateString(const wchar_t *p) +{ + return wcsdup(p); +} + +#endif + +#endif diff --git a/src/util/WStringUtil.cxx b/src/util/WStringUtil.cxx new file mode 100644 index 000000000..19e4fc68d --- /dev/null +++ b/src/util/WStringUtil.cxx @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2003-2015 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 "WStringUtil.hxx" +#include "WStringAPI.hxx" +#include "ASCII.hxx" + +#include <algorithm> + +#include <assert.h> +#include <string.h> + +bool +StringStartsWith(const wchar_t *haystack, const wchar_t *needle) +{ + return memcmp(haystack, needle, StringLength(needle) * sizeof(needle[0])) == 0; +} + +bool +StringEndsWith(const wchar_t *haystack, const wchar_t *needle) +{ + const size_t haystack_length = StringLength(haystack); + const size_t needle_length = StringLength(needle); + + return haystack_length >= needle_length && + StringIsEqual(haystack + haystack_length - needle_length, needle); +} + +const wchar_t * +StringAfterPrefix(const wchar_t *string, const wchar_t *prefix) +{ +#if !CLANG_CHECK_VERSION(3,6) + /* disabled on clang due to -Wtautological-pointer-compare */ + assert(string != nullptr); + assert(prefix != nullptr); +#endif + + size_t prefix_length = StringLength(prefix); + return StringIsEqual(string, prefix, prefix_length) + ? string + prefix_length + : nullptr; +} + +const wchar_t * +FindStringSuffix(const wchar_t *p, const wchar_t *suffix) +{ + const size_t p_length = StringLength(p); + const size_t suffix_length = StringLength(suffix); + + if (p_length < suffix_length) + return nullptr; + + const auto *q = p + p_length - suffix_length; + return memcmp(q, suffix, suffix_length * sizeof(*suffix)) == 0 + ? q + : nullptr; +} diff --git a/src/util/WStringUtil.hxx b/src/util/WStringUtil.hxx new file mode 100644 index 000000000..3dde0162e --- /dev/null +++ b/src/util/WStringUtil.hxx @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2003-2015 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 WSTRING_UTIL_HXX +#define WSTRING_UTIL_HXX + +#include "Compiler.h" + +#include <wchar.h> + +gcc_pure +bool +StringStartsWith(const wchar_t *haystack, const wchar_t *needle); + +gcc_pure +bool +StringEndsWith(const wchar_t *haystack, const wchar_t *needle); + +/** + * Returns the portion of the string after a prefix. If the string + * does not begin with the specified prefix, this function returns + * nullptr. + */ +gcc_nonnull_all +const wchar_t * +StringAfterPrefix(const wchar_t *string, const wchar_t *prefix); + +/** + * Check if the given string ends with the specified suffix. If yes, + * returns the position of the suffix, and nullptr otherwise. + */ +gcc_pure +const wchar_t * +FindStringSuffix(const wchar_t *p, const wchar_t *suffix); + +#endif diff --git a/src/util/bit_reverse.c b/src/util/bit_reverse.c index 9226c4261..271eee166 100644 --- a/src/util/bit_reverse.c +++ b/src/util/bit_reverse.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/bit_reverse.h b/src/util/bit_reverse.h index b39b02e92..f4d378e61 100644 --- a/src/util/bit_reverse.h +++ b/src/util/bit_reverse.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/util/format.c b/src/util/format.c new file mode 100644 index 000000000..66243c8ec --- /dev/null +++ b/src/util/format.c @@ -0,0 +1,259 @@ +/* + * music player command (mpc) + * Copyright (C) 2003-2015 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 "format.h" + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +/** + * Reallocate the given string and append the source string. + */ +gcc_malloc +static char * +string_append(char *dest, const char *src, size_t len) +{ + size_t destlen = dest != NULL + ? strlen(dest) + : 0; + + dest = realloc(dest, destlen + len + 1); + memcpy(dest + destlen, src, len); + dest[destlen + len] = '\0'; + + return dest; +} + +/** + * Skip the format string until the current group is closed by either + * '&', '|' or ']' (supports nesting). + */ +gcc_pure +static const char * +skip_format(const char *p) +{ + unsigned stack = 0; + + while (*p != '\0') { + if (*p == '[') + stack++; + else if (*p == '#' && p[1] != '\0') + /* skip escaped stuff */ + ++p; + else if (stack > 0) { + if (*p == ']') + --stack; + } else if (*p == '&' || *p == '|' || *p == ']') + break; + + ++p; + } + + return p; +} + +static bool +is_name_char(char ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch == '_'; +} + +static char * +format_object2(const char *format, const char **last, const void *object, + const char *(*getter)(const void *object, const char *name)) +{ + char *ret = NULL; + const char *p; + bool found = false; + + for (p = format; *p != '\0';) { + switch (p[0]) { + case '|': + ++p; + if (!found) { + /* nothing found yet: try the next + section */ + free(ret); + ret = NULL; + } else + /* already found a value: skip the + next section */ + p = skip_format(p); + break; + + case '&': + ++p; + if (!found) + /* nothing found yet, so skip this + section */ + p = skip_format(p); + else + /* we found something yet, but it will + only be used if the next section + also found something, so reset the + flag */ + found = false; + break; + + case '[': { + char *t = format_object2(p + 1, &p, object, getter); + if (t != NULL) { + ret = string_append(ret, t, strlen(t)); + free(t); + found = true; + } + } + break; + + case ']': + if (last != NULL) + *last = p + 1; + if (!found) { + free(ret); + ret = NULL; + } + return ret; + + case '\\': { + /* take care of escape sequences */ + char ltemp; + switch (p[1]) { + case 'a': + ltemp = '\a'; + break; + + case 'b': + ltemp = '\b'; + break; + + case 't': + ltemp = '\t'; + break; + + case 'n': + ltemp = '\n'; + break; + + case 'v': + ltemp = '\v'; + break; + + case 'f': + ltemp = '\f'; + break; + + case 'r': + ltemp = '\r'; + break; + + case '[': + case ']': + ltemp = p[1]; + break; + + default: + /* unknown escape: copy the + backslash */ + ltemp = p[0]; + --p; + break; + } + + ret = string_append(ret, <emp, 1); + p += 2; + } + break; + + case '%': { + /* find the extent of this format specifier + (stop at \0, ' ', or esc) */ + const char *end = p + 1; + while (is_name_char(*end)) + ++end; + + const size_t length = end - p + 1; + + if (*end != '%') { + ret = string_append(ret, p, length - 1); + p = end; + continue; + } + + char name[32]; + if (length > (int)sizeof(name)) { + ret = string_append(ret, p, length); + p = end + 1; + continue; + } + + memcpy(name, p + 1, length - 2); + name[length - 2] = 0; + + const char *value = getter(object, name); + size_t value_length; + if (value != NULL) { + if (*value != 0) + found = true; + value_length = strlen(value); + } else { + /* unknown variable: copy verbatim + from format string */ + value = p; + value_length = length; + } + + ret = string_append(ret, value, value_length); + + /* advance past the specifier */ + p = end + 1; + } + break; + + case '#': + /* let the escape character escape itself */ + if (p[1] != '\0') { + ret = string_append(ret, p + 1, 1); + p += 2; + break; + } + + /* fall through */ + + default: + /* pass-through non-escaped portions of the format string */ + ret = string_append(ret, p, 1); + ++p; + } + } + + if (last != NULL) + *last = p; + return ret; +} + +char * +format_object(const char *format, const void *object, + const char *(*getter)(const void *object, const char *name)) +{ + return format_object2(format, NULL, object, getter); +} diff --git a/src/util/format.h b/src/util/format.h new file mode 100644 index 000000000..fa3624b51 --- /dev/null +++ b/src/util/format.h @@ -0,0 +1,51 @@ +/* + * music player command (mpc) + * Copyright (C) 2003-2015 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 MPC_FORMAT_H +#define MPC_FORMAT_H + +#include "Compiler.h" + +struct mpd_song; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Pretty-print an object into a string using the given format + * specification. + * + * @param format the format string + * @param object the object + * @param getter a getter function that extracts a value from the object + * @return the resulting string to be freed by free(); NULL if + * no format string group produced any output + */ +gcc_malloc +char * +format_object(const char *format, const void *object, + const char *(*getter)(const void *object, const char *name)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/win32/Win32Main.cxx b/src/win32/Win32Main.cxx index 75a1e9a23..a833e7362 100644 --- a/src/win32/Win32Main.cxx +++ b/src/win32/Win32Main.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -29,20 +29,19 @@ #include <cstdlib> #include <atomic> -#include <glib.h> - #include <windows.h> +#include <tchar.h> static int service_argc; static char **service_argv; -static char service_name[] = ""; +static TCHAR service_name[] = _T(""); static std::atomic_bool running; static SERVICE_STATUS_HANDLE service_handle; static void WINAPI -service_main(DWORD argc, CHAR *argv[]); +service_main(DWORD argc, LPTSTR argv[]); -static SERVICE_TABLE_ENTRY service_registry[] = { +static constexpr SERVICE_TABLE_ENTRY service_registry[] = { {service_name, service_main}, {nullptr, nullptr} }; @@ -80,21 +79,14 @@ service_dispatcher(gcc_unused DWORD control, gcc_unused DWORD event_type, } static void WINAPI -service_main(gcc_unused DWORD argc, gcc_unused CHAR *argv[]) +service_main(gcc_unused DWORD argc, gcc_unused LPTSTR argv[]) { - DWORD error_code; - gchar* error_message; - service_handle = RegisterServiceCtrlHandlerEx(service_name, service_dispatcher, nullptr); - if (service_handle == 0) { - error_code = GetLastError(); - error_message = g_win32_error_message(error_code); - FormatFatalError("RegisterServiceCtrlHandlerEx() failed: %s", - error_message); - } + if (service_handle == 0) + FatalSystemError("RegisterServiceCtrlHandlerEx() failed"); service_notify_status(SERVICE_START_PENDING); mpd_main(service_argc, service_argv); @@ -131,27 +123,22 @@ console_handler(DWORD event) int win32_main(int argc, char *argv[]) { - DWORD error_code; - gchar* error_message; - service_argc = argc; service_argv = argv; if (StartServiceCtrlDispatcher(service_registry)) return 0; /* run as service successefully */ - error_code = GetLastError(); + const DWORD error_code = GetLastError(); if (error_code == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { /* running as console app */ running.store(false); - SetConsoleTitle("Music Player Daemon"); + SetConsoleTitle(_T("Music Player Daemon")); SetConsoleCtrlHandler(console_handler, TRUE); return mpd_main(argc, argv); } - error_message = g_win32_error_message(error_code); - FormatFatalError("StartServiceCtrlDispatcher() failed: %s", - error_message); + FatalSystemError("StartServiceCtrlDispatcher() failed", error_code); } void win32_app_started() diff --git a/src/zeroconf/AvahiPoll.cxx b/src/zeroconf/AvahiPoll.cxx index 20d5d74e6..1c75cda5a 100644 --- a/src/zeroconf/AvahiPoll.cxx +++ b/src/zeroconf/AvahiPoll.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/AvahiPoll.hxx b/src/zeroconf/AvahiPoll.hxx index e194d3370..ab340565d 100644 --- a/src/zeroconf/AvahiPoll.hxx +++ b/src/zeroconf/AvahiPoll.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/ZeroconfAvahi.cxx b/src/zeroconf/ZeroconfAvahi.cxx index 5adda38f9..46393e9f6 100644 --- a/src/zeroconf/ZeroconfAvahi.cxx +++ b/src/zeroconf/ZeroconfAvahi.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/ZeroconfAvahi.hxx b/src/zeroconf/ZeroconfAvahi.hxx index 09a199f55..2719de528 100644 --- a/src/zeroconf/ZeroconfAvahi.hxx +++ b/src/zeroconf/ZeroconfAvahi.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/ZeroconfBonjour.cxx b/src/zeroconf/ZeroconfBonjour.cxx index 8d7565e0e..3f395f54e 100644 --- a/src/zeroconf/ZeroconfBonjour.cxx +++ b/src/zeroconf/ZeroconfBonjour.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/ZeroconfBonjour.hxx b/src/zeroconf/ZeroconfBonjour.hxx index cff52815e..70d9039c8 100644 --- a/src/zeroconf/ZeroconfBonjour.hxx +++ b/src/zeroconf/ZeroconfBonjour.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/ZeroconfGlue.cxx b/src/zeroconf/ZeroconfGlue.cxx index 95797491b..f00395e7c 100644 --- a/src/zeroconf/ZeroconfGlue.cxx +++ b/src/zeroconf/ZeroconfGlue.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -44,7 +44,7 @@ ZeroconfInit(gcc_unused EventLoop &loop) { const char *serviceName; - zeroconfEnabled = config_get_bool(CONF_ZEROCONF_ENABLED, + zeroconfEnabled = config_get_bool(ConfigOption::ZEROCONF_ENABLED, DEFAULT_ZEROCONF_ENABLED); if (!zeroconfEnabled) return; @@ -56,7 +56,8 @@ ZeroconfInit(gcc_unused EventLoop &loop) return; } - serviceName = config_get_string(CONF_ZEROCONF_NAME, SERVICE_NAME); + serviceName = config_get_string(ConfigOption::ZEROCONF_NAME, + SERVICE_NAME); #ifdef HAVE_AVAHI AvahiInit(loop, serviceName); diff --git a/src/zeroconf/ZeroconfGlue.hxx b/src/zeroconf/ZeroconfGlue.hxx index 5d2f29642..7abd07e65 100644 --- a/src/zeroconf/ZeroconfGlue.hxx +++ b/src/zeroconf/ZeroconfGlue.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/zeroconf/ZeroconfInternal.hxx b/src/zeroconf/ZeroconfInternal.hxx index 4d47d260a..3376062ff 100644 --- a/src/zeroconf/ZeroconfInternal.hxx +++ b/src/zeroconf/ZeroconfInternal.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/src/win32/mpd.ico b/win32/res/mpd.ico Binary files differindex 86fd9fe43..86fd9fe43 100644 --- a/src/win32/mpd.ico +++ b/win32/res/mpd.ico diff --git a/src/win32/mpd_win32_rc.rc.in b/win32/res/mpd.rc.in index e5312dc78..3a33e9981 100644 --- a/src/win32/mpd_win32_rc.rc.in +++ b/win32/res/mpd.rc.in @@ -3,7 +3,7 @@ #define VERSION_NUMBER @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_REVISION@,@VERSION_EXTRA@ #define VERSION_NUMBER_STR "@VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_REVISION@,@VERSION_EXTRA@" -MPD_ICON ICON "@top_srcdir@/src/win32/mpd.ico" +MPD_ICON ICON "@top_srcdir@/win32/res/mpd.ico" 1 VERSIONINFO FILETYPE VFT_APP |