diff options
1094 files changed, 19973 insertions, 11007 deletions
@@ -1,5 +1,5 @@ Music Player Daemon - http://www.musicpd.org -Copyright (C) 2003-2014 The Music Player Daemon Project +Copyright (C) 2003-2015 The Music Player Daemon Project The following people have contributed code to MPD: @@ -18,9 +18,6 @@ Any other C++11 compliant compiler should also work. Boost 1.46 - http://www.boost.org/ -GLib 2.28 - http://www.gtk.org/ -General-purpose utility library. - Optional Output Dependencies ---------------------------- diff --git a/Makefile.am b/Makefile.am index 89819de36..c846d7411 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,10 +1,11 @@ ACLOCAL_AMFLAGS = -I m4 AUTOMAKE_OPTIONS = foreign 1.11 dist-xz subdir-objects -AM_CPPFLAGS += -I$(srcdir)/src $(GLIB_CFLAGS) $(BOOST_CPPFLAGS) +AM_CPPFLAGS += -I$(srcdir)/src $(BOOST_CPPFLAGS) AM_CPPFLAGS += -DSYSTEM_CONFIG_FILE_LOCATION='"$(sysconfdir)/mpd.conf"' +APK_NAME = mpd if ANDROID else bin_PROGRAMS = src/mpd @@ -14,6 +15,7 @@ noinst_LIBRARIES = \ libmpd.a \ libutil.a \ libthread.a \ + libnet.a \ libsystem.a \ libevent.a \ libicu.a \ @@ -46,20 +48,20 @@ src_mpd_LDADD = \ $(DECODER_LIBS) \ $(INPUT_LIBS) \ $(ARCHIVE_LIBS) \ - $(TAG_LIBS) \ $(OUTPUT_LIBS) \ + $(TAG_LIBS) \ $(FILTER_LIBS) \ $(ENCODER_LIBS) \ $(MIXER_LIBS) \ libconf.a \ libevent.a \ libthread.a \ + libnet.a \ + $(FS_LIBS) \ libsystem.a \ - $(ICU_LDADD) \ libutil.a \ - $(FS_LIBS) \ - $(SYSTEMD_DAEMON_LIBS) \ - $(GLIB_LIBS) + $(ICU_LDADD) \ + $(SYSTEMD_DAEMON_LIBS) src_mpd_SOURCES = \ src/Main.cxx src/Main.hxx @@ -79,6 +81,7 @@ libmpd_a_SOURCES = \ src/protocol/Ack.cxx src/protocol/Ack.hxx \ src/protocol/ArgParser.cxx src/protocol/ArgParser.hxx \ src/protocol/Result.cxx src/protocol/Result.hxx \ + src/command/Request.hxx \ src/command/CommandResult.hxx \ src/command/CommandError.cxx src/command/CommandError.hxx \ src/command/AllCommands.cxx src/command/AllCommands.hxx \ @@ -92,7 +95,6 @@ libmpd_a_SOURCES = \ src/command/OtherCommands.cxx src/command/OtherCommands.hxx \ src/command/CommandListBuilder.cxx src/command/CommandListBuilder.hxx \ src/Idle.cxx src/Idle.hxx \ - src/CrossFade.cxx src/CrossFade.hxx \ src/decoder/DecoderError.cxx src/decoder/DecoderError.hxx \ src/decoder/DecoderThread.cxx src/decoder/DecoderThread.hxx \ src/decoder/DecoderCommand.hxx \ @@ -119,6 +121,7 @@ libmpd_a_SOURCES = \ src/client/ClientMessage.cxx src/client/ClientMessage.hxx \ src/client/ClientSubscribe.cxx \ src/client/ClientFile.cxx \ + src/client/Response.cxx src/client/Response.hxx \ src/Listen.cxx src/Listen.hxx \ src/LogInit.cxx src/LogInit.hxx \ src/LogBackend.cxx src/LogBackend.hxx \ @@ -136,9 +139,10 @@ libmpd_a_SOURCES = \ src/Mapper.cxx src/Mapper.hxx \ src/Partition.cxx src/Partition.hxx \ src/Permission.cxx src/Permission.hxx \ - src/PlayerThread.cxx src/PlayerThread.hxx \ - src/PlayerControl.cxx src/PlayerControl.hxx \ - src/PlayerListener.hxx \ + src/player/CrossFade.cxx src/player/CrossFade.hxx \ + src/player/Thread.cxx src/player/Thread.hxx \ + src/player/Control.cxx src/player/Control.hxx \ + src/player/Listener.hxx \ src/PlaylistError.cxx src/PlaylistError.hxx \ src/PlaylistGlobal.cxx src/PlaylistGlobal.hxx \ src/PlaylistPrint.cxx src/PlaylistPrint.hxx \ @@ -164,6 +168,7 @@ libmpd_a_SOURCES = \ src/ReplayGainConfig.cxx src/ReplayGainConfig.hxx \ src/ReplayGainInfo.cxx src/ReplayGainInfo.hxx \ src/DetachedSong.cxx src/DetachedSong.hxx \ + src/LocateUri.cxx src/LocateUri.hxx \ src/SongUpdate.cxx \ src/SongLoader.cxx src/SongLoader.hxx \ src/SongPrint.cxx src/SongPrint.hxx \ @@ -184,9 +189,14 @@ if ANDROID else libmpd_a_SOURCES += \ src/unix/SignalHandlers.cxx src/unix/SignalHandlers.hxx \ - src/unix/Daemon.cxx src/unix/Daemon.hxx \ - src/unix/PidFile.hxx \ src/CommandLine.cxx src/CommandLine.hxx + +if ENABLE_DAEMON +libmpd_a_SOURCES += \ + src/unix/Daemon.cxx src/unix/Daemon.hxx \ + src/unix/PidFile.hxx +endif + endif if ENABLE_DATABASE @@ -266,7 +276,7 @@ libmain_a_CPPFLAGS = $(AM_CPPFLAGS) -Iandroid/build/include src_mpd_LDADD += libandroid.a libjava.a -all-local: android/build/bin/Main-debug.apk +all-local: android/build/bin/$(APK_NAME)-debug.apk clean-local: rm -rf android/build @@ -278,8 +288,8 @@ android/build/build.xml: android/AndroidManifest.xml mkdir -p android/build/include android/build/res android/build/src/org ln -s $(abs_srcdir)/android/AndroidManifest.xml $(abs_srcdir)/android/custom_rules.xml android/build ln -s $(abs_srcdir)/android/src android/build/src/org/musicpd - ln -s $(abs_srcdir)/android/res/values android/build/res - $(ANDROID_SDK)/tools/android update project --path android/build --target android-17 + ln -s $(abs_srcdir)/android/res/values $(abs_srcdir)/android/res/layout android/build/res + $(ANDROID_SDK)/tools/android update project --path android/build --target android-17 --name $(APK_NAME) android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml android/build/res/drawable/icon.png cd android/build && ant compile-jni-classes @@ -303,18 +313,18 @@ APK_DEPS = android/build/res/drawable/icon.png \ $(wildcard $(srcdir)/android/src/*.java) \ android/build/build.xml -android/build/bin/Main-debug.apk: $(APK_DEPS) +android/build/bin/$(APK_NAME)-debug.apk: $(APK_DEPS) cd android/build && ant nodeps debug -android/build/bin/Main-release-unsigned.apk: $(APK_DEPS) +android/build/bin/$(APK_NAME)-release-unsigned.apk: $(APK_DEPS) cd android/build && ant nodeps release -android/build/bin/Main-release-unaligned.apk: android/build/bin/Main-release-unsigned.apk +android/build/bin/$(APK_NAME)-release-unaligned.apk: android/build/bin/$(APK_NAME)-release-unsigned.apk jarsigner -digestalg SHA1 -sigalg MD5withRSA -storepass:env ANDROID_KEYSTORE_PASS -keystore $(ANDROID_KEYSTORE) -signedjar $@ $< $(ANDROID_KEY_ALIAS) ANDROID_SDK_BUILD_TOOLS_VERSION = 20.0.0 -android/build/bin/Main.apk: android/build/bin/Main-release-unaligned.apk +android/build/bin/$(APK_NAME).apk: android/build/bin/$(APK_NAME)-release-unaligned.apk $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/zipalign -f 4 $< $@ endif @@ -323,14 +333,36 @@ endif # Windows resource file # -src/win32/mpd_win32_rc.$(OBJEXT): src/win32/mpd_win32_rc.rc +win32/res/mpd.$(OBJEXT): %.$(OBJEXT): %.rc $(WINDRES) -i $< -o $@ if HAVE_WINDOWS -noinst_DATA = src/win32/mpd_win32_rc.rc +noinst_DATA = win32/res/mpd.rc -EXTRA_src_mpd_DEPENDENCIES = src/win32/mpd_win32_rc.$(OBJEXT) -src_mpd_LDFLAGS = -Wl,src/win32/mpd_win32_rc.$(OBJEXT) +EXTRA_src_mpd_DEPENDENCIES = win32/res/mpd.$(OBJEXT) +src_mpd_LDFLAGS = -Wl,win32/res/mpd.$(OBJEXT) +endif + +# +# Haiku resource file +# + +src/haiku/mpd.rsrc: src/haiku/mpd.rdef + $(RC) -o $@ $< + +if HAVE_HAIKU +noinst_DATA = src/haiku/mpd.rdef + +EXTRA_src_mpd_DEPENDENCIES = src/haiku/mpd.rsrc + +src/mpd.haiku-rsrc-done: src/mpd src/haiku/mpd.rsrc + $(XRES) -o src/mpd src/haiku/mpd.rsrc + @touch src/mpd.haiku-rsrc-done + +all-local: src/mpd.haiku-rsrc-done + +clean-local: + rm -rf src/haiku/mpd.rsrc src/mpd.haiku-rsrc-done endif if ENABLE_DATABASE @@ -346,6 +378,9 @@ endif if ENABLE_SQLITE libmpd_a_SOURCES += \ src/command/StickerCommands.cxx src/command/StickerCommands.hxx \ + src/lib/sqlite/Domain.cxx src/lib/sqlite/Domain.hxx \ + src/lib/sqlite/Util.hxx \ + src/sticker/Match.hxx \ src/sticker/StickerDatabase.cxx src/sticker/StickerDatabase.hxx \ src/sticker/StickerPrint.cxx src/sticker/StickerPrint.hxx \ src/sticker/SongSticker.cxx src/sticker/SongSticker.hxx @@ -357,6 +392,7 @@ libutil_a_SOURCES = \ src/util/Macros.hxx \ src/util/Cast.hxx \ src/util/Clamp.hxx \ + src/util/DeleteDisposer.hxx \ src/util/Alloc.cxx src/util/Alloc.hxx \ src/util/VarSize.hxx \ src/util/Error.cxx src/util/Error.hxx \ @@ -366,7 +402,14 @@ libutil_a_SOURCES = \ src/util/UTF8.cxx src/util/UTF8.hxx \ src/util/CharUtil.hxx \ src/util/NumberParser.hxx \ + src/util/StringPointer.hxx \ + src/util/StringView.cxx src/util/StringView.hxx \ + src/util/AllocatedString.cxx src/util/AllocatedString.hxx \ src/util/StringUtil.cxx src/util/StringUtil.hxx \ + src/util/WStringUtil.cxx src/util/WStringUtil.hxx \ + src/util/StringAPI.hxx \ + src/util/WStringAPI.hxx \ + src/util/DivideString.cxx src/util/DivideString.hxx \ src/util/SplitString.cxx src/util/SplitString.hxx \ src/util/FormatString.cxx src/util/FormatString.hxx \ src/util/Tokenizer.cxx src/util/Tokenizer.hxx \ @@ -387,6 +430,7 @@ libutil_a_SOURCES = \ src/util/OptionParser.cxx src/util/OptionParser.hxx \ src/util/OptionDef.hxx \ src/util/ByteReverse.cxx src/util/ByteReverse.hxx \ + src/util/format.c src/util/format.h \ src/util/bit_reverse.c src/util/bit_reverse.h # Multi-threading library @@ -404,15 +448,24 @@ libthread_a_SOURCES = \ src/thread/Thread.cxx src/thread/Thread.hxx \ src/thread/Id.hxx +# Networking library + +libnet_a_SOURCES = \ + src/net/ToString.cxx src/net/ToString.hxx \ + src/net/Resolver.cxx src/net/Resolver.hxx \ + src/net/StaticSocketAddress.cxx src/net/StaticSocketAddress.hxx \ + src/net/AllocatedSocketAddress.cxx src/net/AllocatedSocketAddress.hxx \ + src/net/SocketAddress.cxx src/net/SocketAddress.hxx \ + src/net/SocketUtil.cxx src/net/SocketUtil.hxx \ + src/net/SocketError.cxx src/net/SocketError.hxx + # System library libsystem_a_SOURCES = \ src/system/ByteOrder.hxx \ src/system/FatalError.cxx src/system/FatalError.hxx \ + src/system/FileDescriptor.cxx src/system/FileDescriptor.hxx \ src/system/fd_util.c src/system/fd_util.h \ - src/system/SocketUtil.cxx src/system/SocketUtil.hxx \ - src/system/SocketError.cxx src/system/SocketError.hxx \ - src/system/Resolver.cxx src/system/Resolver.hxx \ src/system/EventPipe.cxx src/system/EventPipe.hxx \ src/system/EventFD.cxx src/system/EventFD.hxx \ src/system/SignalFD.cxx src/system/SignalFD.hxx \ @@ -445,13 +498,20 @@ libevent_a_SOURCES = \ libicu_a_SOURCES = \ src/lib/icu/Collate.cxx src/lib/icu/Collate.hxx \ + src/lib/icu/Converter.cxx src/lib/icu/Converter.hxx \ src/lib/icu/Error.cxx src/lib/icu/Error.hxx if HAVE_ICU libicu_a_SOURCES += \ + src/lib/icu/Util.cxx src/lib/icu/Util.hxx \ src/lib/icu/Init.cxx src/lib/icu/Init.hxx endif +if HAVE_WINDOWS +libicu_a_SOURCES += \ + src/lib/icu/Win32.cxx src/lib/icu/Win32.hxx +endif + libicu_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(ICU_CFLAGS) @@ -462,6 +522,7 @@ ICU_LDADD = libicu.a $(ICU_LIBS) libpcm_a_SOURCES = \ src/pcm/Domain.cxx src/pcm/Domain.hxx \ src/pcm/Traits.hxx \ + src/pcm/Interleave.cxx src/pcm/Interleave.hxx \ src/pcm/PcmBuffer.cxx src/pcm/PcmBuffer.hxx \ src/pcm/PcmExport.cxx src/pcm/PcmExport.hxx \ src/pcm/PcmConvert.cxx src/pcm/PcmConvert.hxx \ @@ -485,12 +546,12 @@ libpcm_a_SOURCES = \ src/pcm/PcmUtils.hxx libpcm_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(SOXR_CFLAGS) \ - $(SAMPLERATE_CFLAGS) + $(LIBSAMPLERATE_CFLAGS) PCM_LIBS = \ libpcm.a \ $(SOXR_LIBS) \ - $(SAMPLERATE_LIBS) + $(LIBSAMPLERATE_LIBS) if ENABLE_DSD libpcm_a_SOURCES += \ @@ -498,12 +559,12 @@ libpcm_a_SOURCES += \ src/pcm/dsd2pcm/dsd2pcm.c src/pcm/dsd2pcm/dsd2pcm.h endif -if HAVE_LIBSAMPLERATE +if ENABLE_LIBSAMPLERATE libpcm_a_SOURCES += \ src/pcm/LibsamplerateResampler.cxx src/pcm/LibsamplerateResampler.hxx endif -if HAVE_SOXR +if ENABLE_SOXR libpcm_a_SOURCES += \ src/pcm/SoxrResampler.cxx src/pcm/SoxrResampler.hxx endif @@ -527,15 +588,18 @@ libfs_a_SOURCES = \ src/fs/Traits.cxx src/fs/Traits.hxx \ src/fs/Config.cxx src/fs/Config.hxx \ src/fs/Charset.cxx src/fs/Charset.hxx \ - src/fs/Path.cxx src/fs/Path.hxx \ + src/fs/Path.cxx src/fs/Path2.cxx src/fs/Path.hxx \ src/fs/AllocatedPath.cxx src/fs/AllocatedPath.hxx \ + src/fs/NarrowPath.hxx \ src/fs/FileSystem.cxx src/fs/FileSystem.hxx \ + src/fs/FileInfo.hxx \ + src/fs/Glob.hxx \ src/fs/StandardDirectory.cxx src/fs/StandardDirectory.hxx \ src/fs/CheckFile.cxx src/fs/CheckFile.hxx \ src/fs/DirectoryReader.hxx libfs_a_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) -if HAVE_ZLIB +if ENABLE_ZLIB libfs_a_SOURCES += \ src/lib/zlib/Domain.cxx src/lib/zlib/Domain.hxx \ src/fs/io/GunzipReader.cxx src/fs/io/GunzipReader.hxx \ @@ -544,6 +608,11 @@ libfs_a_SOURCES += \ FS_LIBS += $(ZLIB_LIBS) endif +if HAVE_WINDOWS +# for PathMatchSpec() +FS_LIBS += -lshlwapi +endif + # Storage library SMBCLIENT_SOURCES = \ @@ -631,7 +700,7 @@ NEIGHBOR_LIBS = \ $(SMBCLIENT_LIBS) \ libneighbor.a -if HAVE_LIBUPNP +if ENABLE_UPNP libneighbor_a_SOURCES += \ $(UPNP_SOURCES) \ src/neighbor/plugins/UpnpNeighborPlugin.cxx src/neighbor/plugins/UpnpNeighborPlugin.hxx @@ -670,7 +739,7 @@ libdb_plugins_a_SOURCES = \ src/db/plugins/simple/SimpleDatabasePlugin.cxx \ src/db/plugins/simple/SimpleDatabasePlugin.hxx -if HAVE_LIBMPDCLIENT +if ENABLE_LIBMPDCLIENT libdb_plugins_a_SOURCES += \ src/db/plugins/ProxyDatabasePlugin.cxx src/db/plugins/ProxyDatabasePlugin.hxx endif @@ -679,7 +748,7 @@ DB_LIBS = \ libdb_plugins.a \ $(LIBMPDCLIENT_LIBS) -if HAVE_LIBUPNP +if ENABLE_UPNP libdb_plugins_a_SOURCES += \ $(UPNP_SOURCES) \ src/db/plugins/upnp/UpnpDatabasePlugin.cxx src/db/plugins/upnp/UpnpDatabasePlugin.hxx \ @@ -701,6 +770,7 @@ if ENABLE_ARCHIVE noinst_LIBRARIES += libarchive.a libmpd_a_SOURCES += \ + src/TagArchive.cxx src/TagArchive.hxx \ src/db/update/Archive.cxx libarchive_a_SOURCES = \ @@ -722,19 +792,19 @@ ARCHIVE_LIBS = \ $(ISO9660_LIBS) \ $(ZZIP_LIBS) -if HAVE_BZ2 +if ENABLE_BZ2 libarchive_a_SOURCES += \ src/archive/plugins/Bzip2ArchivePlugin.cxx \ src/archive/plugins/Bzip2ArchivePlugin.hxx endif -if HAVE_ZZIP +if ENABLE_ZZIP libarchive_a_SOURCES += \ src/archive/plugins/ZzipArchivePlugin.cxx \ src/archive/plugins/ZzipArchivePlugin.hxx endif -if HAVE_ISO9660 +if ENABLE_ISO9660 libarchive_a_SOURCES += \ src/archive/plugins/Iso9660ArchivePlugin.cxx \ src/archive/plugins/Iso9660ArchivePlugin.hxx @@ -749,7 +819,9 @@ endif libconf_a_SOURCES = \ src/config/ConfigDefaults.hxx \ src/config/ConfigPath.cxx src/config/ConfigPath.hxx \ - src/config/ConfigData.cxx src/config/ConfigData.hxx \ + src/config/Data.cxx src/config/Data.hxx \ + src/config/Block.cxx src/config/Block.hxx \ + src/config/Param.cxx src/config/Param.hxx \ src/config/ConfigParser.cxx src/config/ConfigParser.hxx \ src/config/ConfigGlobal.cxx src/config/ConfigGlobal.hxx \ src/config/ConfigFile.cxx src/config/ConfigFile.hxx \ @@ -771,13 +843,15 @@ libtag_a_SOURCES =\ src/tag/TagBuilder.cxx src/tag/TagBuilder.hxx \ src/tag/TagItem.hxx \ src/tag/TagHandler.cxx src/tag/TagHandler.hxx \ - src/tag/TagSettings.c src/tag/TagSettings.h \ + src/tag/Mask.hxx \ + src/tag/Settings.cxx src/tag/Settings.hxx \ src/tag/TagConfig.cxx src/tag/TagConfig.hxx \ src/tag/TagNames.c \ src/tag/TagString.cxx src/tag/TagString.hxx \ src/tag/TagPool.cxx src/tag/TagPool.hxx \ src/tag/TagTable.cxx src/tag/TagTable.hxx \ src/tag/Set.cxx src/tag/Set.hxx \ + src/tag/Format.cxx src/tag/Format.hxx \ src/tag/VorbisComment.cxx src/tag/VorbisComment.hxx \ src/tag/ReplayGain.cxx src/tag/ReplayGain.hxx \ src/tag/MixRamp.cxx src/tag/MixRamp.hxx \ @@ -785,7 +859,7 @@ libtag_a_SOURCES =\ src/tag/ApeReplayGain.cxx src/tag/ApeReplayGain.hxx \ src/tag/ApeTag.cxx src/tag/ApeTag.hxx -if HAVE_ID3TAG +if ENABLE_ID3TAG libtag_a_SOURCES += \ src/tag/TagId3.cxx src/tag/TagId3.hxx \ src/tag/TagRva2.cxx src/tag/TagRva2.hxx \ @@ -795,9 +869,14 @@ endif # ffmpeg -if HAVE_FFMPEG +if ENABLE_FFMPEG noinst_LIBRARIES += libffmpeg.a libffmpeg_a_SOURCES = \ + src/lib/ffmpeg/Init.cxx src/lib/ffmpeg/Init.hxx \ + src/lib/ffmpeg/Time.hxx \ + src/lib/ffmpeg/Buffer.hxx \ + src/lib/ffmpeg/LogError.cxx src/lib/ffmpeg/LogError.hxx \ + src/lib/ffmpeg/LogCallback.cxx src/lib/ffmpeg/LogCallback.hxx \ src/lib/ffmpeg/Error.cxx src/lib/ffmpeg/Error.hxx \ src/lib/ffmpeg/Domain.cxx src/lib/ffmpeg/Domain.hxx libffmpeg_a_CPPFLAGS = $(AM_CPPFLAGS) \ @@ -861,25 +940,25 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/DsdLib.hxx endif -if HAVE_MAD +if ENABLE_MAD libdecoder_a_SOURCES += \ src/decoder/plugins/MadDecoderPlugin.cxx \ src/decoder/plugins/MadDecoderPlugin.hxx endif -if HAVE_MPG123 +if ENABLE_MPG123 libdecoder_a_SOURCES += \ src/decoder/plugins/Mpg123DecoderPlugin.cxx \ src/decoder/plugins/Mpg123DecoderPlugin.hxx endif -if HAVE_MPCDEC +if ENABLE_MPCDEC libdecoder_a_SOURCES += \ src/decoder/plugins/MpcdecDecoderPlugin.cxx \ src/decoder/plugins/MpcdecDecoderPlugin.hxx endif -if HAVE_OPUS +if ENABLE_OPUS libdecoder_a_SOURCES += \ src/decoder/plugins/OggUtil.cxx \ src/decoder/plugins/OggUtil.hxx \ @@ -895,19 +974,19 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/OpusDecoderPlugin.h endif -if HAVE_WAVPACK +if ENABLE_WAVPACK libdecoder_a_SOURCES += \ src/decoder/plugins/WavpackDecoderPlugin.cxx \ src/decoder/plugins/WavpackDecoderPlugin.hxx endif -if HAVE_ADPLUG +if ENABLE_ADPLUG libdecoder_a_SOURCES += \ src/decoder/plugins/AdPlugDecoderPlugin.cxx \ src/decoder/plugins/AdPlugDecoderPlugin.h endif -if HAVE_FAAD +if ENABLE_FAAD libdecoder_a_SOURCES += \ src/decoder/plugins/FaadDecoderPlugin.cxx src/decoder/plugins/FaadDecoderPlugin.hxx endif @@ -925,7 +1004,7 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/VorbisDecoderPlugin.cxx src/decoder/plugins/VorbisDecoderPlugin.h endif -if HAVE_FLAC +if ENABLE_FLAC libdecoder_a_SOURCES += \ src/decoder/plugins/FlacInput.cxx src/decoder/plugins/FlacInput.hxx \ src/decoder/plugins/FlacIOHandle.cxx src/decoder/plugins/FlacIOHandle.hxx \ @@ -937,7 +1016,7 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/FlacDecoderPlugin.h endif -if HAVE_AUDIOFILE +if ENABLE_AUDIOFILE libdecoder_a_SOURCES += \ src/decoder/plugins/AudiofileDecoderPlugin.cxx \ src/decoder/plugins/AudiofileDecoderPlugin.hxx @@ -949,7 +1028,7 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/MikmodDecoderPlugin.hxx endif -if HAVE_MODPLUG +if ENABLE_MODPLUG libmodplug_decoder_plugin_a_SOURCES = \ src/decoder/plugins/ModplugDecoderPlugin.cxx \ src/decoder/plugins/ModplugDecoderPlugin.hxx @@ -977,8 +1056,10 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/WildmidiDecoderPlugin.hxx endif -if HAVE_FFMPEG +if ENABLE_FFMPEG libdecoder_a_SOURCES += \ + src/decoder/plugins/FfmpegIo.cxx \ + src/decoder/plugins/FfmpegIo.hxx \ src/decoder/plugins/FfmpegMetaData.cxx \ src/decoder/plugins/FfmpegMetaData.hxx \ src/decoder/plugins/FfmpegDecoderPlugin.cxx \ @@ -991,7 +1072,7 @@ libdecoder_a_SOURCES += \ src/decoder/plugins/SndfileDecoderPlugin.hxx endif -if HAVE_GME +if ENABLE_GME libdecoder_a_SOURCES += \ src/decoder/plugins/GmeDecoderPlugin.cxx src/decoder/plugins/GmeDecoderPlugin.hxx endif @@ -1021,7 +1102,9 @@ ENCODER_LIBS = \ libencoder_plugins_a_SOURCES = \ src/encoder/EncoderAPI.hxx \ + src/encoder/EncoderInterface.hxx \ src/encoder/EncoderPlugin.hxx \ + src/encoder/ToOutputStream.cxx src/encoder/ToOutputStream.hxx \ src/encoder/plugins/OggStream.hxx \ src/encoder/plugins/NullEncoderPlugin.cxx \ src/encoder/plugins/NullEncoderPlugin.hxx \ @@ -1040,25 +1123,25 @@ libencoder_plugins_a_SOURCES += \ src/encoder/plugins/WaveEncoderPlugin.hxx endif -if ENABLE_VORBIS_ENCODER +if ENABLE_VORBISENC libencoder_plugins_a_SOURCES += \ src/encoder/plugins/VorbisEncoderPlugin.cxx \ src/encoder/plugins/VorbisEncoderPlugin.hxx endif -if HAVE_OPUS +if ENABLE_OPUS libencoder_plugins_a_SOURCES += \ src/encoder/plugins/OpusEncoderPlugin.cxx \ src/encoder/plugins/OpusEncoderPlugin.hxx endif -if ENABLE_LAME_ENCODER +if ENABLE_LAME libencoder_plugins_a_SOURCES += \ src/encoder/plugins/LameEncoderPlugin.cxx \ src/encoder/plugins/LameEncoderPlugin.hxx endif -if ENABLE_TWOLAME_ENCODER +if ENABLE_TWOLAME libencoder_plugins_a_SOURCES += \ src/encoder/plugins/TwolameEncoderPlugin.cxx \ src/encoder/plugins/TwolameEncoderPlugin.hxx @@ -1070,7 +1153,7 @@ libencoder_plugins_a_SOURCES += \ src/encoder/plugins/FlacEncoderPlugin.hxx endif -if ENABLE_SHINE_ENCODER +if ENABLE_SHINE libencoder_plugins_a_SOURCES += \ src/encoder/plugins/ShineEncoderPlugin.cxx \ src/encoder/plugins/ShineEncoderPlugin.hxx @@ -1134,7 +1217,7 @@ INPUT_LIBS = \ $(FFMPEG_LIBS2) \ $(MMS_LIBS) -if HAVE_ALSA +if ENABLE_ALSA libinput_a_SOURCES += \ src/input/plugins/AlsaInputPlugin.cxx \ src/input/plugins/AlsaInputPlugin.hxx @@ -1167,7 +1250,7 @@ libinput_a_SOURCES += \ src/input/plugins/CdioParanoiaInputPlugin.hxx endif -if HAVE_FFMPEG +if ENABLE_FFMPEG libinput_a_SOURCES += \ src/input/plugins/FfmpegInputPlugin.cxx src/input/plugins/FfmpegInputPlugin.hxx endif @@ -1194,12 +1277,12 @@ OUTPUT_LIBS = \ $(ROAR_LIBS) \ $(JACK_LIBS) \ $(OPENAL_LIBS) \ - $(PULSE_LIBS) \ $(SHOUT_LIBS) OUTPUT_API_SRC = \ src/output/OutputAPI.hxx \ src/output/Internal.hxx \ + src/output/Wrapper.hxx \ src/output/Registry.cxx src/output/Registry.hxx \ src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx \ src/output/OutputThread.cxx \ @@ -1219,7 +1302,7 @@ liboutput_plugins_a_SOURCES = \ MIXER_LIBS = \ libmixer_plugins.a \ - $(PULSE_LIBS) + $(PULSE_LIBS2) MIXER_API_SRC = \ src/mixer/Listener.hxx \ @@ -1231,13 +1314,14 @@ MIXER_API_SRC = \ src/mixer/MixerInternal.hxx libmixer_plugins_a_SOURCES = \ + src/mixer/plugins/NullMixerPlugin.cxx \ src/mixer/plugins/SoftwareMixerPlugin.cxx \ src/mixer/plugins/SoftwareMixerPlugin.hxx libmixer_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(ALSA_CFLAGS) \ $(PULSE_CFLAGS) -if HAVE_ALSA +if ENABLE_ALSA liboutput_plugins_a_SOURCES += \ src/output/plugins/AlsaOutputPlugin.cxx \ src/output/plugins/AlsaOutputPlugin.hxx @@ -1255,14 +1339,14 @@ liboutput_plugins_a_SOURCES += \ OUTPUT_LIBS += -lOpenSLES endif -if HAVE_ROAR +if ENABLE_ROAR liboutput_plugins_a_SOURCES += \ src/output/plugins/RoarOutputPlugin.cxx \ src/output/plugins/RoarOutputPlugin.hxx libmixer_plugins_a_SOURCES += src/mixer/plugins/RoarMixerPlugin.cxx endif -if HAVE_AO +if ENABLE_AO liboutput_plugins_a_SOURCES += \ src/output/plugins/AoOutputPlugin.cxx \ src/output/plugins/AoOutputPlugin.hxx @@ -1274,13 +1358,21 @@ liboutput_plugins_a_SOURCES += \ src/output/plugins/FifoOutputPlugin.hxx endif +if HAVE_HAIKU +liboutput_plugins_a_SOURCES += \ + src/output/plugins/HaikuOutputPlugin.cxx \ + src/output/plugins/HaikuOutputPlugin.hxx +libmixer_plugins_a_SOURCES += \ + src/mixer/plugins/HaikuMixerPlugin.cxx src/mixer/plugins/HaikuMixerPlugin.hxx +endif + if ENABLE_PIPE_OUTPUT liboutput_plugins_a_SOURCES += \ src/output/plugins/PipeOutputPlugin.cxx \ src/output/plugins/PipeOutputPlugin.hxx endif -if HAVE_JACK +if ENABLE_JACK liboutput_plugins_a_SOURCES += \ src/output/plugins/JackOutputPlugin.cxx \ src/output/plugins/JackOutputPlugin.hxx @@ -1305,12 +1397,21 @@ liboutput_plugins_a_SOURCES += \ src/output/plugins/OSXOutputPlugin.hxx endif -if HAVE_PULSE +if ENABLE_PULSE liboutput_plugins_a_SOURCES += \ src/output/plugins/PulseOutputPlugin.cxx \ src/output/plugins/PulseOutputPlugin.hxx libmixer_plugins_a_SOURCES += \ src/mixer/plugins/PulseMixerPlugin.cxx src/mixer/plugins/PulseMixerPlugin.hxx + +noinst_LIBRARIES += libpulse.a +libpulse_a_SOURCES = \ + src/lib/pulse/LogError.cxx src/lib/pulse/LogError.hxx \ + src/lib/pulse/Error.cxx src/lib/pulse/Error.hxx \ + src/lib/pulse/Domain.cxx src/lib/pulse/Domain.hxx +libpulse_a_CPPFLAGS = $(AM_CPPFLAGS) $(PULSE_CFLAGS) +PULSE_LIBS2 = libpulse.a $(PULSE_LIBS) +OUTPUT_LIBS += $(PULSE_LIBS2) endif if HAVE_SHOUT @@ -1361,15 +1462,12 @@ libplaylist_plugins_a_SOURCES = \ src/playlist/CloseSongEnumerator.hxx \ src/playlist/MemorySongEnumerator.cxx \ src/playlist/MemorySongEnumerator.hxx \ - src/playlist/cue/CueParser.cxx src/playlist/cue/CueParser.hxx \ src/playlist/plugins/ExtM3uPlaylistPlugin.cxx \ src/playlist/plugins/ExtM3uPlaylistPlugin.hxx \ src/playlist/plugins/M3uPlaylistPlugin.cxx \ src/playlist/plugins/M3uPlaylistPlugin.hxx \ - src/playlist/plugins/CuePlaylistPlugin.cxx \ - src/playlist/plugins/CuePlaylistPlugin.hxx \ - src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx \ - src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx \ + src/playlist/plugins/PlsPlaylistPlugin.cxx \ + src/playlist/plugins/PlsPlaylistPlugin.hxx \ src/playlist/PlaylistRegistry.cxx src/playlist/PlaylistRegistry.hxx libplaylist_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \ $(EXPAT_CFLAGS) \ @@ -1381,6 +1479,21 @@ PLAYLIST_LIBS = \ $(EXPAT_LIBS) \ $(FLAC_LIBS) +if ENABLE_FLAC +libplaylist_plugins_a_SOURCES += \ + src/playlist/plugins/FlacPlaylistPlugin.cxx \ + src/playlist/plugins/FlacPlaylistPlugin.hxx +endif + +if ENABLE_CUE +libplaylist_plugins_a_SOURCES += \ + src/playlist/cue/CueParser.cxx src/playlist/cue/CueParser.hxx \ + src/playlist/plugins/CuePlaylistPlugin.cxx \ + src/playlist/plugins/CuePlaylistPlugin.hxx \ + src/playlist/plugins/EmbeddedCuePlaylistPlugin.cxx \ + src/playlist/plugins/EmbeddedCuePlaylistPlugin.hxx +endif + if ENABLE_SOUNDCLOUD libplaylist_plugins_a_SOURCES += \ src/playlist/plugins/SoundCloudPlaylistPlugin.cxx \ @@ -1388,7 +1501,7 @@ libplaylist_plugins_a_SOURCES += \ PLAYLIST_LIBS += $(YAJL_LIBS) endif -if HAVE_EXPAT +if ENABLE_EXPAT libplaylist_plugins_a_SOURCES += \ src/lib/expat/ExpatParser.cxx src/lib/expat/ExpatParser.hxx \ src/playlist/plugins/XspfPlaylistPlugin.cxx \ @@ -1399,12 +1512,6 @@ libplaylist_plugins_a_SOURCES += \ src/playlist/plugins/RssPlaylistPlugin.hxx endif -if HAVE_GLIB -libplaylist_plugins_a_SOURCES += \ - src/playlist/plugins/PlsPlaylistPlugin.cxx \ - src/playlist/plugins/PlsPlaylistPlugin.hxx -endif - # # Filter plugins # @@ -1456,7 +1563,9 @@ C_TESTS = \ test/test_mixramp \ test/test_pcm \ test/test_protocol \ - test/test_queue_priority + test/test_queue_priority \ + test/TestFs \ + test/TestIcu if ENABLE_CURL C_TESTS += test/test_icy_parser @@ -1477,10 +1586,12 @@ noinst_PROGRAMS = \ test/read_conf \ test/run_resolver \ test/run_input \ + test/WriteFile \ test/dump_text_file \ test/dump_playlist \ test/run_decoder \ test/read_tags \ + test/ReadApeTags \ test/run_filter \ test/run_output \ test/run_convert \ @@ -1504,11 +1615,11 @@ if ENABLE_ARCHIVE noinst_PROGRAMS += test/visit_archive endif -if HAVE_ID3TAG +if ENABLE_ID3TAG noinst_PROGRAMS += test/dump_rva2 endif -if HAVE_ALSA +if ENABLE_ALSA # this debug program is still ALSA specific noinst_PROGRAMS += test/read_mixer endif @@ -1516,17 +1627,16 @@ endif test_read_conf_LDADD = \ libconf.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_read_conf_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ test/read_conf.cxx test_run_resolver_LDADD = \ - libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libnet.a \ + libutil.a test_run_resolver_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ test/run_resolver.cxx @@ -1541,8 +1651,7 @@ test_DumpDatabase_LDADD = \ libevent.a \ $(FS_LIBS) \ libsystem.a \ - $(ICU_LDADD) \ - $(GLIB_LIBS) + $(ICU_LDADD) test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \ src/protocol/Ack.cxx \ src/Log.cxx src/LogBackend.cxx \ @@ -1556,18 +1665,18 @@ test_DumpDatabase_SOURCES = test/DumpDatabase.cxx \ src/TagSave.cxx \ src/SongFilter.cxx -if HAVE_LIBUPNP +if ENABLE_UPNP test_DumpDatabase_SOURCES += src/lib/expat/ExpatParser.cxx endif test_run_storage_LDADD = \ $(STORAGE_LIBS) \ $(FS_LIBS) \ + $(ICU_LDADD) \ libevent.a \ libthread.a \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_run_storage_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ src/IOThread.cxx \ @@ -1581,15 +1690,14 @@ test_run_input_LDADD = \ $(ARCHIVE_LIBS) \ $(TAG_LIBS) \ libconf.a \ - libutil.a \ libevent.a \ libthread.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - $(GLIB_LIBS) + libutil.a test_run_input_SOURCES = test/run_input.cxx \ test/ScopeIOThread.hxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/IOThread.cxx \ src/TagSave.cxx @@ -1601,7 +1709,6 @@ test_run_neighbor_explorer_SOURCES = \ src/IOThread.cxx \ test/run_neighbor_explorer.cxx test_run_neighbor_explorer_LDADD = $(AM_LDADD) \ - $(GLIB_LIBS) \ $(NEIGHBOR_LIBS) \ $(INPUT_LIBS) \ $(ARCHIVE_LIBS) \ @@ -1609,11 +1716,12 @@ test_run_neighbor_explorer_LDADD = $(AM_LDADD) \ libconf.a \ libevent.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ libthread.a \ libutil.a -if HAVE_LIBUPNP +if ENABLE_UPNP test_run_neighbor_explorer_SOURCES += src/lib/expat/ExpatParser.cxx endif @@ -1626,12 +1734,12 @@ test_visit_archive_LDADD = \ $(ARCHIVE_LIBS) \ $(TAG_LIBS) \ libconf.a \ - libutil.a \ libevent.a \ libthread.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - $(GLIB_LIBS) + libutil.a test_visit_archive_SOURCES = test/visit_archive.cxx \ test/ScopeIOThread.hxx \ src/Log.cxx src/LogBackend.cxx \ @@ -1640,7 +1748,7 @@ test_visit_archive_SOURCES = test/visit_archive.cxx \ endif -if HAVE_ZLIB +if ENABLE_ZLIB noinst_PROGRAMS += test/run_gzip test/run_gunzip @@ -1652,13 +1760,22 @@ test_run_gzip_SOURCES = test/run_gzip.cxx test_run_gunzip_SOURCES = test/run_gunzip.cxx \ src/Log.cxx src/LogBackend.cxx test_run_gunzip_LDADD = \ - $(GLIB_LIBS) \ libutil.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a endif +test_WriteFile_LDADD = \ + $(FS_LIBS) \ + $(ICU_LDADD) \ + libsystem.a \ + libutil.a +test_WriteFile_SOURCES = \ + src/Log.cxx src/LogBackend.cxx \ + test/WriteFile.cxx + test_dump_text_file_LDADD = \ $(INPUT_LIBS) \ $(ARCHIVE_LIBS) \ @@ -1666,13 +1783,12 @@ test_dump_text_file_LDADD = \ libconf.a \ libevent.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ libthread.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_dump_text_file_SOURCES = test/dump_text_file.cxx \ test/ScopeIOThread.hxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/IOThread.cxx @@ -1687,10 +1803,10 @@ test_dump_playlist_LDADD = \ libevent.a \ libthread.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ libutil.a \ - libpcm.a \ - $(GLIB_LIBS) + libpcm.a test_dump_playlist_SOURCES = test/dump_playlist.cxx \ test/FakeDecoderAPI.cxx test/FakeDecoderAPI.hxx \ test/ScopeIOThread.hxx \ @@ -1702,7 +1818,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.cxx \ src/AudioFormat.cxx src/CheckAudioFormat.cxx \ src/DetachedSong.cxx -if HAVE_FLAC +if ENABLE_FLAC test_dump_playlist_SOURCES += \ src/ReplayGainInfo.cxx \ src/decoder/plugins/FlacMetadata.cxx @@ -1718,13 +1834,12 @@ test_run_decoder_LDADD = \ libevent.a \ libthread.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_run_decoder_SOURCES = test/run_decoder.cxx \ test/FakeDecoderAPI.cxx test/FakeDecoderAPI.hxx \ test/ScopeIOThread.hxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/IOThread.cxx \ src/ReplayGainInfo.cxx \ @@ -1744,9 +1859,9 @@ test_read_tags_LDADD = \ libevent.a \ libthread.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_read_tags_SOURCES = test/read_tags.cxx \ test/FakeDecoderAPI.cxx test/FakeDecoderAPI.hxx \ test/ScopeIOThread.hxx \ @@ -1756,11 +1871,17 @@ test_read_tags_SOURCES = test/read_tags.cxx \ src/AudioFormat.cxx src/CheckAudioFormat.cxx \ $(DECODER_SRC) -if HAVE_ID3TAG +test_ReadApeTags_LDADD = \ + $(TAG_LIBS) \ + $(FS_LIBS) \ + $(ICU_LDADD) \ + libutil.a +test_ReadApeTags_SOURCES = test/ReadApeTags.cxx + +if ENABLE_ID3TAG test_dump_rva2_LDADD = \ $(TAG_LIBS) \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_dump_rva2_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ test/dump_rva2.cxx @@ -1770,12 +1891,11 @@ test_run_filter_LDADD = \ $(FILTER_LIBS) \ libconf.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_run_filter_SOURCES = test/run_filter.cxx \ test/FakeReplayGainConfig.cxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/filter/FilterPlugin.cxx src/filter/FilterRegistry.cxx \ src/CheckAudioFormat.cxx \ @@ -1786,7 +1906,6 @@ test_run_filter_SOURCES = test/run_filter.cxx \ if ENABLE_ENCODER noinst_PROGRAMS += test/run_encoder test_run_encoder_SOURCES = test/run_encoder.cxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/CheckAudioFormat.cxx \ src/AudioFormat.cxx \ @@ -1798,15 +1917,14 @@ test_run_encoder_LDADD = \ libpcm.a \ libthread.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a endif -if ENABLE_VORBIS_ENCODER +if ENABLE_VORBISENC noinst_PROGRAMS += test/test_vorbis_encoder test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.cxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/CheckAudioFormat.cxx \ src/AudioFormat.cxx \ @@ -1820,20 +1938,18 @@ test_test_vorbis_encoder_LDADD = $(MPD_LIBS) \ $(TAG_LIBS) \ libconf.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a endif test_software_volume_SOURCES = test/software_volume.cxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/AudioFormat.cxx src/CheckAudioFormat.cxx \ src/AudioParser.cxx test_software_volume_LDADD = \ $(PCM_LIBS) \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_run_avahi_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ @@ -1846,17 +1962,14 @@ test_run_avahi_LDADD = \ libevent.a \ libsystem.a \ libutil.a \ - $(GLIB_LIBS) \ $(AVAHI_LIBS) test_run_normalize_SOURCES = test/run_normalize.cxx \ - test/stdbin.h \ src/CheckAudioFormat.cxx \ src/AudioCompress/compress.c \ src/AudioParser.cxx test_run_normalize_LDADD = \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_run_convert_SOURCES = test/run_convert.cxx \ src/Log.cxx src/LogBackend.cxx \ @@ -1865,8 +1978,11 @@ test_run_convert_SOURCES = test/run_convert.cxx \ src/AudioParser.cxx test_run_convert_LDADD = \ $(PCM_LIBS) \ + libconf.a \ + $(FS_LIBS) \ + libsystem.a \ libutil.a \ - $(GLIB_LIBS) + $(ICU_LDADD) test_run_output_LDADD = $(MPD_LIBS) \ $(PCM_LIBS) \ @@ -1878,14 +1994,14 @@ test_run_output_LDADD = $(MPD_LIBS) \ libconf.a \ libevent.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ + libnet.a \ libsystem.a \ libthread.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_run_output_SOURCES = test/run_output.cxx \ test/FakeReplayGainConfig.cxx \ test/ScopeIOThread.hxx \ - test/stdbin.h \ src/Log.cxx src/LogBackend.cxx \ src/IOThread.cxx \ src/CheckAudioFormat.cxx \ @@ -1907,9 +2023,9 @@ test_read_mixer_LDADD = \ libconf.a \ libevent.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a test_read_mixer_SOURCES = test/read_mixer.cxx \ src/Log.cxx src/LogBackend.cxx \ src/mixer/MixerControl.cxx \ @@ -1939,11 +2055,13 @@ test_run_inotify_SOURCES = test/run_inotify.cxx \ test_run_inotify_LDADD = \ libevent.a \ libsystem.a \ - libutil.a \ - $(GLIB_LIBS) + libutil.a endif test_test_util_SOURCES = \ + test/DivideStringTest.hxx \ + test/SplitStringTest.hxx \ + test/UriUtilTest.hxx \ test/TestCircularBuffer.hxx \ test/test_util.cxx test_test_util_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTTI=0 @@ -1966,7 +2084,6 @@ test_test_rewind_SOURCES = \ test_test_rewind_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTTI=0 test_test_rewind_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations test_test_rewind_LDADD = \ - $(GLIB_LIBS) \ $(INPUT_LIBS) \ libthread.a \ libtag.a \ @@ -1980,7 +2097,6 @@ test_test_mixramp_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTT test_test_mixramp_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations test_test_mixramp_LDADD = \ libutil.a \ - $(GLIB_LIBS) \ $(CPPUNIT_LIBS) if ENABLE_CURL @@ -1992,7 +2108,6 @@ test_test_icy_parser_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declaration test_test_icy_parser_LDADD = \ libtag.a \ libutil.a \ - $(GLIB_LIBS) \ $(CPPUNIT_LIBS) endif @@ -2005,6 +2120,7 @@ test_test_pcm_SOURCES = \ test/test_pcm_format.cxx \ test/test_pcm_volume.cxx \ test/test_pcm_mix.cxx \ + test/test_pcm_interleave.cxx \ test/test_pcm_export.cxx \ test/test_pcm_all.hxx \ test/test_pcm_main.cxx @@ -2013,8 +2129,7 @@ test_test_pcm_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations test_test_pcm_LDADD = \ $(PCM_LIBS) \ libutil.a \ - $(CPPUNIT_LIBS) \ - $(GLIB_LIBS) + $(CPPUNIT_LIBS) test_test_archive_SOURCES = \ src/Log.cxx src/LogBackend.cxx \ @@ -2024,7 +2139,6 @@ test_test_archive_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations test_test_archive_LDADD = \ libarchive.a \ libutil.a \ - $(GLIB_LIBS) \ $(CPPUNIT_LIBS) if ENABLE_DATABASE @@ -2034,6 +2148,7 @@ test_test_translate_song_SOURCES = \ src/PlaylistError.cxx \ src/DetachedSong.cxx \ src/SongLoader.cxx \ + src/LocateUri.cxx \ src/Log.cxx \ test/test_translate_song.cxx test_test_translate_song_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTTI=0 @@ -2042,9 +2157,9 @@ test_test_translate_song_LDADD = \ $(STORAGE_LIBS) \ libtag.a \ $(FS_LIBS) \ + $(ICU_LDADD) \ libsystem.a \ libutil.a \ - $(GLIB_LIBS) \ $(CPPUNIT_LIBS) endif @@ -2070,6 +2185,23 @@ test_test_queue_priority_LDADD = \ libutil.a \ $(CPPUNIT_LIBS) +test_TestFs_SOURCES = \ + test/TestFs.cxx +test_TestFs_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTTI=0 +test_TestFs_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations +test_TestFs_LDADD = \ + $(FS_LIBS) \ + $(CPPUNIT_LIBS) + +test_TestIcu_SOURCES = \ + test/TestIcu.cxx +test_TestIcu_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTTI=0 +test_TestIcu_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations +test_TestIcu_LDADD = \ + $(ICU_LDADD) \ + libutil.a \ + $(CPPUNIT_LIBS) + if ENABLE_DSD noinst_PROGRAMS += src/pcm/dsd2pcm/dsd2pcm @@ -2159,4 +2291,5 @@ EXTRA_DIST = $(doc_DATA) autogen.sh \ android/src/Bridge.java \ android/src/Loader.java \ android/src/Main.java \ - src/win32/mpd_win32_rc.rc.in src/win32/mpd.ico + win32/res/mpd.rc.in win32/res/mpd.ico \ + src/haiku/App_MusicPD @@ -1,3 +1,47 @@ +ver 0.20 (not yet released) +* protocol + - "commands" returns playlist commands only if playlist_directory configured + - "search"/"find" have a "window" parameter + - report song duration with milliseconds precision + - "sticker find" can match sticker values + - drop the "file:///" prefix for absolute file paths +* tags + - ape, ogg: drop support for non-standard tag "album artist" + affected filetypes: vorbis, flac, opus & all files with ape2 tags + (most importantly some mp3s) + - id3: remove the "id3v1_encoding" setting; by definition, all ID3v1 tags + are ISO-Latin-1 +* decoder + - ffmpeg: support ReplayGain and MixRamp + - ffmpeg: support stream tags + - gme: add option "accuracy" + - mad: reduce memory usage while scanning tags + - mpcdec: read the bit rate +* playlist + - cue: don't skip pregap + - embcue: fix last track + - flac: new plugin which reads the "CUESHEET" metadata block +* output + - jack: reduce CPU usage + - pulse: set channel map to WAVE-EX + - recorder: record tags + - recorder: allow dynamic file names +* mixer + - null: new plugin +* resampler + - new block "resampler" in configuration file + replacing the old "samplerate_converter" setting + - soxr: allow multi-threaded resampling +* reset song priority on playback +* write database and state file atomically +* always write UTF-8 to the log file. +* remove dependency on GLib +* support libsystemd (instead of the older libsystemd-daemon) +* database + - proxy: add TCP keepalive option +* update + - apply .mpdignore matches to subdirectories + ver 0.19.11 (2015/10/27) * tags - ape: fix buffer overflow diff --git a/android/build.py b/android/build.py index 351e881cf..024651e8f 100755 --- a/android/build.py +++ b/android/build.py @@ -22,15 +22,31 @@ if not os.path.isdir(ndk_path): print("NDK not found in", ndk_path, file=sys.stderr) sys.exit(1) +# select the NDK target +ndk_arch = 'arm' +host_arch = 'arm-linux-androideabi' +android_abi = 'armeabi-v7a' +ndk_platform = 'android-14' + +# select the NDK compiler +gcc_version = '4.9' +llvm_version = '3.5' + # the path to the MPD sources mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..')) # output directories lib_path = os.path.abspath('lib') -tarball_path = lib_path -src_path = os.path.join(lib_path, 'src') -build_path = os.path.join(lib_path, 'build') -root_path = os.path.join(lib_path, 'root') + +shared_path = lib_path +if 'MPD_SHARED_LIB' in os.environ: + shared_path = os.environ['MPD_SHARED_LIB'] +tarball_path = os.path.join(shared_path, 'download') +src_path = os.path.join(shared_path, 'src') + +arch_path = os.path.join(lib_path, host_arch) +build_path = os.path.join(arch_path, 'build') +root_path = os.path.join(arch_path, 'root') # build host configuration build_arch = 'linux-x86_64' @@ -39,16 +55,6 @@ build_arch = 'linux-x86_64' # one on the build host os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(root_path, 'lib/pkgconfig') -# select the NDK compiler -gcc_version = '4.9' -llvm_version = '3.5' - -# select the NDK target -ndk_arch = 'arm' -host_arch = 'arm-linux-androideabi' -android_abi = 'armeabi-v7a' -ndk_platform = 'android-14' - # set up the NDK toolchain gcc_toolchain = os.path.join(ndk_path, 'toolchains', host_arch + '-' + gcc_version, 'prebuilt', build_arch) @@ -299,8 +305,8 @@ thirdparty_libs = [ ), AutotoolsProject( - 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.4.tar.xz', - '55f2288055e44754275a17c9a2497391', + 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz', + '28cb28097c07a735d6af56e598e1c90f', 'lib/libvorbis.a', ['--disable-shared', '--enable-static'], ), @@ -341,15 +347,14 @@ thirdparty_libs = [ ), FfmpegProject( - 'http://ffmpeg.org/releases/ffmpeg-2.5.tar.bz2', - '4346fe710cc6bdd981f6534d2420d1ab', + 'http://ffmpeg.org/releases/ffmpeg-2.6.2.tar.bz2', + 'e75d598921285d6775f20164a91936ac', 'lib/libavcodec.a', [ '--disable-shared', '--enable-static', '--enable-gpl', '--enable-small', '--disable-pthreads', - '--disable-runtime-cpudetect', '--disable-programs', '--disable-doc', '--disable-avdevice', @@ -366,8 +371,8 @@ thirdparty_libs = [ ), AutotoolsProject( - 'http://curl.haxx.se/download/curl-7.39.0.tar.lzma', - 'e9aa6dec29920eba8ef706ea5823bad7', + 'http://curl.haxx.se/download/curl-7.42.1.tar.lzma', + 'f1e460020a99da039b153e4bf0cd8600', 'lib/libcurl.a', [ '--disable-shared', '--enable-static', @@ -388,8 +393,8 @@ thirdparty_libs = [ ), BoostProject( - 'http://netcologne.dl.sourceforge.net/project/boost/boost/1.55.0/boost_1_55_0.tar.bz2', - 'd6eef4b4cacb2183f2bf265a5a03a354', + 'http://netcologne.dl.sourceforge.net/project/boost/boost/1.58.0/boost_1_58_0.tar.bz2', + 'b8839650e61e9c1c0a89f371dd475546', 'include/boost/version.hpp', ), ] @@ -420,13 +425,8 @@ configure = [ '--enable-silent-rules', - '--disable-glib', '--disable-icu', - # disabled for now because these features require GLib: - '--disable-httpd-output', - '--disable-vorbis-encoder', - ] + configure_args subprocess.check_call(configure) diff --git a/android/src/Bridge.java b/android/src/Bridge.java index 537343b56..4faf706bb 100644 --- a/android/src/Bridge.java +++ b/android/src/Bridge.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/android/src/Loader.java b/android/src/Loader.java index fdaa27753..75d4857a3 100644 --- a/android/src/Loader.java +++ b/android/src/Loader.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/android/src/Main.java b/android/src/Main.java index d87f7709b..a4d81ba74 100644 --- a/android/src/Main.java +++ b/android/src/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/configure.ac b/configure.ac index 3d29e724f..ea8b1320f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ AC_PREREQ(2.60) -AC_INIT(mpd, 0.19.11, musicpd-dev-team@lists.sourceforge.net) +AC_INIT(mpd, 0.20, musicpd-dev-team@lists.sourceforge.net) VERSION_MAJOR=0 -VERSION_MINOR=19 -VERSION_REVISION=11 +VERSION_MINOR=20 +VERSION_REVISION=0 VERSION_EXTRA=0 AC_CONFIG_SRCDIR([src/Main.cxx]) @@ -14,7 +14,7 @@ AM_SILENT_RULES AC_CONFIG_HEADERS(config.h) AC_CONFIG_MACRO_DIR([m4]) -AC_DEFINE(PROTOCOL_VERSION, "0.19.0", [The MPD protocol version]) +AC_DEFINE(PROTOCOL_VERSION, "0.20.0", [The MPD protocol version]) GIT_COMMIT=`GIT_DIR="$srcdir/.git" git describe --dirty --always 2>/dev/null` if test x$GIT_COMMIT != x; then @@ -97,11 +97,13 @@ linux*) mingw32* | windows*) AC_CONFIG_FILES([ - src/win32/mpd_win32_rc.rc + win32/res/mpd.rc ]) AC_CHECK_TOOL(WINDRES, windres) AM_CPPFLAGS="$AM_CPPFLAGS -DWIN32_LEAN_AND_MEAN" AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0600 -D_WIN32_WINNT=0x0600" + AM_CPPFLAGS="$AM_CPPFLAGS -DSTRICT" + AM_CPPFLAGS="$AM_CPPFLAGS -DUNICODE -D_UNICODE" LIBS="$LIBS -lws2_32" host_is_windows=yes host_is_unix=no @@ -114,6 +116,10 @@ darwin*) solaris*) host_is_solaris=yes ;; +haiku*) + AC_CHECK_TOOL(RC, rc) + AC_CHECK_TOOL(XRES, xres) + ;; esac AM_CONDITIONAL([ANDROID], [test x$host_is_android = xyes]) @@ -193,19 +199,37 @@ dnl --------------------------------------------------------------------------- dnl Header/Library Checks dnl --------------------------------------------------------------------------- +AX_PTHREAD +LIBS="$PTHREAD_LIBS $LIBS" +AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS" +AM_CXXFLAGS="$AM_CXXFLAGS $PTHREAD_CFLAGS" + +MPD_WITH_LIBRARY([PTHREAD], + [AC_CHECK_FUNCS([pthread_setname_np])]) + AC_SEARCH_LIBS([clock_gettime], [rt]) -AC_SEARCH_LIBS([syslog], [bsd socket inet], - [AC_DEFINE(HAVE_SYSLOG, 1, [Define if syslog() is available])]) +AC_ARG_ENABLE(syslog, + AS_HELP_STRING([--enable-syslog], + [enable syslog support (default: auto)]),, + enable_syslog=auto) +MPD_AUTO(syslog, [syslog support], [syslog() not available], + [AC_SEARCH_LIBS([syslog], [bsd socket inet], + [found_syslog=yes], + [found_syslog=no])]) +if test x$enable_syslog = xyes; then + AC_DEFINE(HAVE_SYSLOG, 1, [Define if syslog() is available]) +fi -AC_SEARCH_LIBS([socket], [socket]) +AC_SEARCH_LIBS([socket], [network socket]) AC_SEARCH_LIBS([gethostbyname], [nsl]) if test x$host_is_linux = xyes; then - AC_CHECK_FUNCS(pipe2 accept4) + AC_CHECK_FUNCS(pipe2 accept4 linkat) fi AC_CHECK_FUNCS(getpwnam_r getpwuid_r) +AC_CHECK_FUNCS(fnmatch) AC_CHECK_FUNCS(strndup) if test x$host_is_linux = xyes; then @@ -221,18 +245,6 @@ AC_CHECK_HEADERS(valgrind/memcheck.h) AC_CHECK_HEADERS([sys/prctl.h], AC_CHECK_FUNCS([prctl])) -AX_PTHREAD -LIBS="$PTHREAD_LIBS $LIBS" -AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS" -AM_CXXFLAGS="$AM_CXXFLAGS $PTHREAD_CFLAGS" - -AC_CHECK_LIB([pthread], [pthread_setname_np], - [have_pthread_setname_np=yes], - [have_pthread_setname_np=no]) -if test x$have_pthread_setname_np = xyes; then - AC_DEFINE(HAVE_PTHREAD_SETNAME_NP, 1, [Is pthread_setname_np() available?]) -fi - dnl --------------------------------------------------------------------------- dnl Event loop selection dnl --------------------------------------------------------------------------- @@ -283,90 +295,23 @@ AC_ARG_ENABLE(database, AS_HELP_STRING([--enable-database], [enable support for the music database]),, enable_database=yes) -AM_CONDITIONAL(ENABLE_DATABASE, test x$enable_database = xyes) +MPD_DEFINE_CONDITIONAL(enable_database, ENABLE_DATABASE, + [the music database]) if test x$enable_database = xyes; then database_auto=auto - AC_DEFINE(ENABLE_DATABASE, 1, [Define to enable the music database]) else database_auto=no fi -AC_ARG_ENABLE(libmpdclient, - AS_HELP_STRING([--enable-libmpdclient], - [enable support for the MPD client]),, - enable_libmpdclient=auto) -MPD_DEPENDS([enable_libmpdclient], [enable_database], - [Cannot use --enable-libmpdclient with --disable-database]) - -AC_ARG_ENABLE(expat, - AS_HELP_STRING([--enable-expat], - [enable the expat XML parser]),, - enable_expat=auto) - -AC_ARG_ENABLE(upnp, - AS_HELP_STRING([--enable-upnp], - [enable UPnP client support (default: auto)]),, - enable_upnp=auto) -MPD_DEPENDS([enable_upnp], [enable_database], - [Cannot use --enable-upnp with --disable-database]) - -AC_ARG_ENABLE(adplug, - AS_HELP_STRING([--enable-adplug], - [enable the AdPlug decoder plugin (default: auto)]),, - enable_adplug=auto) - -AC_ARG_ENABLE(alsa, - AS_HELP_STRING([--enable-alsa], [enable ALSA support]),, - [enable_alsa=$linux_auto]) - -AC_ARG_ENABLE(roar, - AS_HELP_STRING([--enable-roar], - [enable support for RoarAudio]),, - [enable_roar=auto]) - -AC_ARG_ENABLE(ao, - AS_HELP_STRING([--enable-ao], - [enable support for libao]),, - enable_ao=auto) -MPD_DEPENDS([enable_ao], [enable_glib], - [Cannot use --enable-ao with --disable-glib]) - -AC_ARG_ENABLE(audiofile, - AS_HELP_STRING([--enable-audiofile], - [enable audiofile support (WAV and others)]),, - enable_audiofile=auto) - -AC_ARG_ENABLE(zlib, - AS_HELP_STRING([--enable-zlib], - [enable zlib support (default: auto)]),, - enable_zlib=auto) - -AC_ARG_ENABLE(bzip2, - AS_HELP_STRING([--enable-bzip2], - [enable bzip2 archive support (default: auto)]),, - enable_bzip2=auto) - -AC_ARG_ENABLE(cdio-paranoia, - AS_HELP_STRING([--enable-cdio-paranoia], - [enable support for audio CD support]),, - enable_cdio_paranoia=auto) -MPD_DEPENDS([enable_cdio_paranoia], [enable_glib], - [Cannot use --enable-cdio-paranoia with --disable-glib]) - -AC_ARG_ENABLE(curl, - AS_HELP_STRING([--enable-curl], - [enable support for libcurl HTTP streaming (default: auto)]),, - [enable_curl=auto]) - -AC_ARG_ENABLE(smbclient, - AS_HELP_STRING([--enable-smbclient], - [enable support for libsmbclient (default: auto)]),, - [enable_smbclient=auto]) - -AC_ARG_ENABLE(nfs, - AS_HELP_STRING([--enable-nfs], - [enable support for libnfs (default: auto)]),, - [enable_nfs=auto]) +default_enable_daemon=yes +if test x$host_is_android = xyes || test x$host_is_android = xyes; then + default_enable_daemon=no +fi +AC_ARG_ENABLE(daemon, + AS_HELP_STRING([--enable-daemon], + [enable daemonization (default: enabled)]),, + enable_daemon=$default_enable_daemon) +MPD_DEFINE_CONDITIONAL(enable_daemon, ENABLE_DAEMON, [Enable daemonization?]) AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], @@ -383,44 +328,20 @@ AC_ARG_ENABLE(dsd, [enable DSD decoder (default: enable)]),, [enable_dsd=yes]) -AC_ARG_ENABLE(ffmpeg, - AS_HELP_STRING([--enable-ffmpeg], - [enable FFMPEG support]),, - enable_ffmpeg=auto) - AC_ARG_ENABLE(fifo, AS_HELP_STRING([--disable-fifo], [disable support for writing audio to a FIFO (default: enable)]),, enable_fifo=yes) -AC_ARG_ENABLE(flac, - AS_HELP_STRING([--enable-flac], - [enable FLAC decoder]),, - enable_flac=auto) - -AC_ARG_ENABLE(fluidsynth, - AS_HELP_STRING([--enable-fluidsynth], - [enable MIDI support via fluidsynth (default: auto)]),, - enable_fluidsynth=auto) - -AC_ARG_ENABLE(gme, - AS_HELP_STRING([--enable-gme], - [enable Blargg's game music emulator plugin]),, - enable_gme=auto) -MPD_DEPENDS([enable_gme], [enable_glib], - [Cannot use --enable-gme with --disable-glib]) +AC_ARG_ENABLE(haiku, + AS_HELP_STRING([--enable-haiku], + [enable the Haiku output plugin (default: auto)]),, + enable_haiku=auto) AC_ARG_ENABLE(httpd-output, AS_HELP_STRING([--enable-httpd-output], [enables the HTTP server output]),, [enable_httpd_output=auto]) -MPD_DEPENDS([enable_httpd_output], [enable_glib], - [Cannot use --enable-httpd-output with --disable-glib]) - -AC_ARG_ENABLE(id3, - AS_HELP_STRING([--enable-id3], - [enable id3 support]),, - enable_id3=auto) AC_ARG_ENABLE(inotify, AS_HELP_STRING([--disable-inotify], @@ -432,86 +353,27 @@ AC_ARG_ENABLE(ipv6, [disable IPv6 support (default: enable)]),, [enable_ipv6=yes]) -AC_ARG_ENABLE(iso9660, - AS_HELP_STRING([--enable-iso9660], - [enable iso9660 archive support (default: disabled)]),, - enable_iso9660=no) - -AC_ARG_ENABLE(jack, - AS_HELP_STRING([--enable-jack], - [enable jack support]),, - enable_jack=auto) -MPD_DEPENDS([enable_jack], [enable_glib], - [Cannot use --enable-jack with --disable-glib]) - AC_SYS_LARGEFILE AC_ARG_ENABLE(soundcloud, AS_HELP_STRING([--enable-soundcloud], [enable support for soundcloud.com]),, [enable_soundcloud=auto]) -MPD_DEPENDS([enable_soundcloud], [enable_glib], - [Cannot use --enable-soundcloud with --disable-glib]) - -AC_ARG_ENABLE(lame-encoder, - AS_HELP_STRING([--enable-lame-encoder], - [enable the LAME mp3 encoder]),, - enable_lame_encoder=auto) AC_ARG_ENABLE([libwrap], AS_HELP_STRING([--enable-libwrap], [use libwrap]),, [enable_libwrap=auto]) -AC_ARG_ENABLE(lsr, - AS_HELP_STRING([--enable-lsr], - [enable libsamplerate support]),, - enable_lsr=auto) - -AC_ARG_ENABLE(soxr, - AS_HELP_STRING([--enable-soxr], - [enable the libsoxr resampler]),, - enable_soxr=auto) - -AC_ARG_ENABLE(mad, - AS_HELP_STRING([--enable-mad], - [enable libmad mp3 decoder plugin]),, - enable_mad=auto) - AC_ARG_ENABLE(mikmod, AS_HELP_STRING([--enable-mikmod], [enable the mikmod decoder (default: disable)]),, enable_mikmod=no) -AC_ARG_ENABLE(mms, - AS_HELP_STRING([--enable-mms], - [enable the MMS protocol with libmms]),, - [enable_mms=auto]) - -AC_ARG_ENABLE(modplug, - AS_HELP_STRING([--enable-modplug], - [enable modplug decoder plugin]),, - enable_modplug=auto) - -AC_ARG_ENABLE(mpc, - AS_HELP_STRING([--enable-mpc], - [disable musepack (MPC) support (default: auto)]),, - enable_mpc=auto) - -AC_ARG_ENABLE(mpg123, - AS_HELP_STRING([--enable-mpg123], - [enable libmpg123 decoder plugin]),, - enable_mpg123=auto) - AC_ARG_ENABLE(openal, AS_HELP_STRING([--enable-openal], [enable OpenAL support (default: auto)]),, enable_openal=auto) -AC_ARG_ENABLE(opus, - AS_HELP_STRING([--enable-opus], - [enable Opus codec support (default: auto)]),, - enable_opus=auto) - AC_ARG_ENABLE(oss, AS_HELP_STRING([--disable-oss], [disable OSS support (default: enable)]),, @@ -527,11 +389,6 @@ AC_ARG_ENABLE(pipe-output, [enable support for writing audio to a pipe (default: disable)]),, enable_pipe_output=no) -AC_ARG_ENABLE(pulse, - AS_HELP_STRING([--enable-pulse], - [enable support for the PulseAudio sound server]),, - enable_pulse=auto) - AC_ARG_ENABLE(recorder-output, AS_HELP_STRING([--enable-recorder-output], [enables the recorder file output plugin (default: disable)]),, @@ -541,41 +398,17 @@ AC_ARG_ENABLE(sidplay, AS_HELP_STRING([--enable-sidplay], [enable C64 SID support via libsidplay2]),, enable_sidplay=auto) -MPD_DEPENDS([enable_sidplay], [enable_glib], - [Cannot use --enable-sidplay with --disable-glib]) - -AC_ARG_ENABLE(shine-encoder, - AS_HELP_STRING([--enable-shine-encoder], - [enables shine encoder]),, - [enable_shine_encoder=auto]) AC_ARG_ENABLE(shout, AS_HELP_STRING([--enable-shout], [enables the shoutcast streaming output]),, [enable_shout=auto]) -AC_ARG_ENABLE(sndfile, - AS_HELP_STRING([--enable-sndfile], - [enable sndfile support]),, - enable_sndfile=auto) - AC_ARG_ENABLE(solaris_output, AS_HELP_STRING([--enable-solaris-output], [enables the Solaris /dev/audio output]),, [enable_solaris_output=$host_is_solaris]) -AC_ARG_ENABLE(sqlite, - AS_HELP_STRING([--enable-sqlite], - [enable support for the SQLite database]),, - [enable_sqlite=$database_auto]) -MPD_DEPENDS([enable_sqlite], [enable_glib], - [Cannot use --enable-sqlite with --disable-glib]) - -AC_ARG_ENABLE(systemd-daemon, - AS_HELP_STRING([--enable-systemd-daemon], - [use the systemd daemon library (default=auto)]),, - [enable_systemd_daemon=$linux_auto]) - AC_ARG_ENABLE(tcp, AS_HELP_STRING([--disable-tcp], [disable support for clients connecting via TCP (default: enable)]),, @@ -586,16 +419,6 @@ AC_ARG_ENABLE(test, [build the test programs (default: disabled)]),, enable_test=no) -AC_ARG_WITH(tremor, - AS_HELP_STRING([--with-tremor=PFX], - [use Tremor (vorbisidec) integer Ogg Vorbis decoder (with optional prefix)]),, - with_tremor=no) - -AC_ARG_ENABLE(twolame-encoder, - AS_HELP_STRING([--enable-twolame-encoder], - [enable the TwoLAME mp2 encoder]),, - enable_twolame_encoder=auto) - AC_ARG_ENABLE(un, AS_HELP_STRING([--disable-un], [disable support for clients connecting via unix domain sockets (default: enable)]),, @@ -606,56 +429,21 @@ AC_ARG_ENABLE(vorbis, [enable Ogg Vorbis decoder]),, enable_vorbis=auto) -AC_ARG_ENABLE(vorbis-encoder, - AS_HELP_STRING([--enable-vorbis-encoder], - [enable the Ogg Vorbis encoder]),, - [enable_vorbis_encoder=auto]) -MPD_DEPENDS([enable_vorbis_encoder], [enable_glib], - [Cannot use --enable-vorbis-encoder with --disable-glib]) - AC_ARG_ENABLE(wave-encoder, AS_HELP_STRING([--enable-wave-encoder], [enable the PCM wave encoder]),, enable_wave_encoder=yes) -AC_ARG_ENABLE(wavpack, - AS_HELP_STRING([--enable-wavpack], - [enable WavPack support]),, - enable_wavpack=auto) -MPD_DEPENDS([enable_wavpack], [enable_glib], - [Cannot use --enable-wavpack with --disable-glib]) - AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], [treat warnings as errors (default: disabled)]),, enable_werror=no) -AC_ARG_ENABLE(wildmidi, - AS_HELP_STRING([--enable-wildmidi], - [enable MIDI support via wildmidi (default: auto)]),, - enable_wildmidi=auto) - AC_ARG_WITH(zeroconf, AS_HELP_STRING([--with-zeroconf=@<:@auto|avahi|bonjour|no@:>@], [enable zeroconf backend (default=auto)]),, with_zeroconf="auto") -AC_ARG_ENABLE(zzip, - AS_HELP_STRING([--enable-zzip], - [enable zip archive support (default: disabled)]),, - enable_zzip=no) - - -AC_ARG_WITH(tremor-libraries, - AS_HELP_STRING([--with-tremor-libraries=DIR], - [directory where Tremor library is installed (optional)]),, - tremor_libraries="") - -AC_ARG_WITH(tremor-includes, - AS_HELP_STRING([--with-tremor-includes=DIR], - [directory where Tremor header files are installed (optional)]),, - tremor_includes="") - dnl --------------------------------------------------------------------------- dnl Mandatory Libraries dnl --------------------------------------------------------------------------- @@ -683,34 +471,25 @@ CPPFLAGS="$CPPFLAGS_SAVED" AC_ARG_ENABLE(icu, AS_HELP_STRING([--enable-icu], - [enable libicu for Unicode (default: enabled)]),, - enable_icu=yes) - -if test x$enable_icu = xyes; then - PKG_CHECK_MODULES([ICU], [icu-i18n],, - [AC_MSG_ERROR([libicu not found])]) - - AC_DEFINE(HAVE_ICU, 1, [Define if libicu is used]) -fi -AM_CONDITIONAL(HAVE_ICU, test x$enable_icu = xyes) + [enable libicu for Unicode (default: auto)]),, + enable_icu=auto) -AC_ARG_ENABLE(glib, - AS_HELP_STRING([--enable-glib], - [enable GLib usage (default: enabled)]),, - enable_glib=yes) +MPD_AUTO_PKG(icu, ICU, [icu-i18n], [libicu], [libicu not found]) +MPD_DEFINE_CONDITIONAL(enable_icu, HAVE_ICU, [libicu]) -if test x$enable_glib = xyes; then - PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.28 gthread-2.0],, - [AC_MSG_ERROR([GLib 2.28 is required])]) +AC_ARG_ENABLE(iconv, + AS_HELP_STRING([--enable-iconv], + [enable iconv for character set conversion (default: auto)]),, + enable_iconv=yes) - if test x$GCC = xyes; then - # suppress warnings in the GLib headers - GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'` - fi +if test x$enable_icu = xyes; then + dnl We don't need iconv() if we have libicu + enable_iconv=no +fi - AC_DEFINE(HAVE_GLIB, 1, [Define if GLib is used]) +if test x$enable_iconv = xyes; then + AC_CHECK_FUNCS(iconv) fi -AM_CONDITIONAL(HAVE_GLIB, test x$enable_glib = xyes) dnl --------------------------------------------------------------------------- dnl Protocol Options @@ -761,12 +540,16 @@ if AC_MSG_ERROR([No client interfaces configured!]) fi -MPD_AUTO_PKG(systemd_daemon, SYSTEMD_DAEMON, libsystemd-daemon, - [systemd activation], [libsystemd-daemon not found]) -AM_CONDITIONAL(ENABLE_SYSTEMD_DAEMON, test x$enable_systemd_daemon = xyes) -if test x$enable_systemd_daemon = xyes; then - AC_DEFINE([ENABLE_SYSTEMD_DAEMON], 1, [Define to use the systemd daemon library]) -fi +MPD_ENABLE_AUTO(systemd_daemon, SYSTEMD_DAEMON, [systemd socket activation], + [libsystemd not found], [$linux_auto], [ + dnl Check for libsystemd and fall back to (the older) + dnl libsystemd-daemon + PKG_CHECK_MODULES([SYSTEMD_DAEMON], [libsystemd], + [found_systemd_daemon=yes], + [PKG_CHECK_MODULES([SYSTEMD_DAEMON], [libsystemd-daemon], + [found_systemd_daemon=yes], + [found_systemd_daemon=no])]) +]) dnl --------------------------------------------------------------------------- dnl LIBC Features @@ -780,22 +563,14 @@ dnl Miscellaneous Libraries dnl --------------------------------------------------------------------------- dnl -------------------------------- libmpdclient -------------------------------- -MPD_AUTO_PKG(libmpdclient, LIBMPDCLIENT, [libmpdclient >= 2.2], - [MPD client library], [libmpdclient not found]) -if test x$enable_libmpdclient = xyes; then - AC_DEFINE(HAVE_LIBMPDCLIENT, 1, [Define to use libmpdclient]) -fi - -AM_CONDITIONAL(HAVE_LIBMPDCLIENT, test x$enable_libmpdclient = xyes) +MPD_ENABLE_AUTO_PKG_DEPENDS(libmpdclient, LIBMPDCLIENT, + [libmpdclient >= 2.2], + [MPD client library], [libmpdclient not found], [], + [enable_database], [Cannot use --enable-libmpdclient with --disable-database]) dnl -------------------------------- expat -------------------------------- -MPD_AUTO_PKG(expat, EXPAT, [expat], +MPD_ENABLE_AUTO_PKG(expat, EXPAT, [expat], [expat XML parser], [expat not found]) -if test x$enable_expat = xyes; then - AC_DEFINE(HAVE_EXPAT, 1, [Define to use the expat XML parser]) -fi - -AM_CONDITIONAL(HAVE_EXPAT, test x$enable_expat = xyes) dnl --------------------------------- inotify --------------------------------- AC_CHECK_FUNCS(inotify_init inotify_init1) @@ -804,10 +579,7 @@ if test x$ac_cv_func_inotify_init = xno; then enable_inotify=no fi -if test x$enable_inotify = xyes; then - AC_DEFINE([ENABLE_INOTIFY], 1, [Define to enable inotify support]) -fi -AM_CONDITIONAL(ENABLE_INOTIFY, test x$enable_inotify = xyes) +MPD_DEFINE_CONDITIONAL(enable_inotify, ENABLE_INOTIFY, [inotify support]) dnl --------------------------------- libwrap --------------------------------- if test x$enable_libwrap != xno; then @@ -847,13 +619,9 @@ dnl Metadata Plugins dnl --------------------------------------------------------------------------- dnl -------------------------------- libid3tag -------------------------------- -MPD_AUTO_PKG_LIB(id3, ID3TAG, id3tag, id3tag, id3_file_open, [-lid3tag -lz], [], - [id3tag], [libid3tag not found]) -if test x$enable_id3 = xyes; then - AC_DEFINE(HAVE_ID3TAG, 1, [Define to use id3tag]) -fi - -AM_CONDITIONAL(HAVE_ID3TAG, test x$enable_id3 = xyes) +MPD_ENABLE_AUTO_PKG_LIB(id3, ID3TAG, + id3tag, id3tag, id3_file_open, [-lid3tag -lz], [], + [ID3 support using libid3tag], [libid3tag not found]) dnl --------------------------------------------------------------------------- dnl Autodiscovery @@ -879,11 +647,10 @@ esac MPD_AUTO_PKG(avahi, AVAHI, [avahi-client dbus-1], [avahi client library], [avahi-client not found]) if test x$enable_avahi = xyes; then - AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support]) with_zeroconf=avahi fi -AM_CONDITIONAL(HAVE_AVAHI, test x$enable_avahi = xyes) +MPD_DEFINE_CONDITIONAL(enable_avahi, HAVE_AVAHI, [Avahi Zeroconf]) enable_bounjour=no if test x$with_zeroconf != xno; then @@ -916,99 +683,62 @@ dnl --------------------------------------------------------------------------- dnl ---------------------------------- sqlite --------------------------------- -MPD_AUTO_PKG(sqlite, SQLITE, [sqlite3], - [SQLite database support], [sqlite not found]) -if test x$enable_sqlite = xyes; then - AC_DEFINE([ENABLE_SQLITE], 1, [Define to enable sqlite database support]) -fi - -AM_CONDITIONAL(ENABLE_SQLITE, test x$enable_sqlite = xyes) +MPD_ENABLE_AUTO_PKG(sqlite, SQLITE, [sqlite3 >= 3.7.3], + [SQLite database support], [sqlite not found], + [$database_auto]) dnl --------------------------------------------------------------------------- dnl Converter Plugins dnl --------------------------------------------------------------------------- dnl ------------------------------ libsamplerate ------------------------------ -MPD_AUTO_PKG(lsr, SAMPLERATE, [samplerate >= 0.1.3], +MPD_ENABLE_AUTO_PKG(lsr, LIBSAMPLERATE, [samplerate >= 0.1.3], [libsamplerate resampling], [libsamplerate not found]) -if test x$enable_lsr = xyes; then - AC_DEFINE([HAVE_LIBSAMPLERATE], 1, - [Define to enable libsamplerate]) -fi -AM_CONDITIONAL(HAVE_LIBSAMPLERATE, test x$enable_lsr = xyes) dnl ------------------------------ libsoxr ------------------------------------ -MPD_AUTO_PKG(soxr, SOXR, [soxr], +MPD_ENABLE_AUTO_PKG(soxr, SOXR, [soxr], [libsoxr resampler], [libsoxr not found]) -if test x$enable_soxr = xyes; then - AC_DEFINE([HAVE_SOXR], 1, [Define to enable libsoxr]) -fi - -AM_CONDITIONAL(HAVE_SOXR, test x$enable_soxr = xyes) dnl --------------------------------------------------------------------------- dnl Input Plugins dnl --------------------------------------------------------------------------- dnl ----------------------------------- CURL ---------------------------------- -MPD_AUTO_PKG(curl, CURL, [libcurl >= 7.18], +MPD_ENABLE_AUTO_PKG(curl, CURL, [libcurl >= 7.18], [libcurl HTTP streaming], [libcurl not found]) -if test x$enable_curl = xyes; then - AC_DEFINE(ENABLE_CURL, 1, [Define when libcurl is used for HTTP streaming]) -fi -AM_CONDITIONAL(ENABLE_CURL, test x$enable_curl = xyes) dnl ----------------------------------- smbclient ----------------------------- -MPD_AUTO_PKG_LIB(smbclient, SMBCLIENT, [smbclient >= 0.2], +MPD_ENABLE_AUTO_PKG_LIB(smbclient, SMBCLIENT, [smbclient >= 0.2], [smbclient], [smbc_init], [-lsmbclient], [], [smbclient input plugin], [libsmbclient not found]) -if test x$enable_smbclient = xyes; then - AC_DEFINE(ENABLE_SMBCLIENT, 1, [Define when libsmbclient is used]) -fi -AM_CONDITIONAL(ENABLE_SMBCLIENT, test x$enable_smbclient = xyes) dnl ----------------------------------- NFS ----------------------------- -MPD_AUTO_PKG(nfs, NFS, [libnfs], +MPD_ENABLE_AUTO_PKG(nfs, NFS, [libnfs], [NFS input plugin], [libnfs not found]) -if test x$enable_nfs = xyes; then - AC_DEFINE(ENABLE_NFS, 1, [Define when libnfs is used]) -fi -AM_CONDITIONAL(ENABLE_NFS, test x$enable_nfs = xyes) dnl --------------------------------- Soundcloud ------------------------------ -if test x$enable_soundcloud != xno; then - PKG_CHECK_MODULES([YAJL], [yajl >= 2.0], +MPD_AUTO([soundcloud], [soundcloud.com support], [libyajl not found], + [PKG_CHECK_MODULES([YAJL], [yajl >= 2.0], [found_soundcloud=yes], - AC_CHECK_LIB([yajl], [yajl_parse_complete], - [found_soundcloud=yes YAJL_CFLAGS=-DHAVE_YAJL1 YAJL_LIBS=-lyajl], - [found_soundcloud=no])) -fi -MPD_AUTO_RESULT([soundcloud], [soundcloud.com support], [libyajl not found]) -if test x$enable_soundcloud = xyes; then - AC_DEFINE(ENABLE_SOUNDCLOUD, 1, [Define when soundcloud is enabled]) -fi -AM_CONDITIONAL(ENABLE_SOUNDCLOUD, test x$enable_soundcloud = xyes) -AC_SUBST(YAJL_LIBS) + [found_soundcloud=no])]) +MPD_DEFINE_CONDITIONAL(enable_soundcloud, ENABLE_SOUNDCLOUD, + [soundcloud.com support]) dnl ---------------------------------- cdio --------------------------------- -MPD_AUTO_PKG(cdio_paranoia, CDIO_PARANOIA, [libcdio_paranoia], - [libcdio_paranoia audio CD library], [libcdio_paranoia not found]) +MPD_ENABLE_AUTO_PKG(cdio_paranoia, CDIO_PARANOIA, [libcdio_paranoia], + [libcdio_paranoia input plugin], [libcdio_paranoia not found]) if test x$enable_cdio_paranoia = xyes; then - AC_DEFINE([ENABLE_CDIO_PARANOIA], 1, - [Define to enable libcdio_paranoia support]) AC_CHECK_HEADERS(cdio/paranoia/paranoia.h) fi -AM_CONDITIONAL(ENABLE_CDIO_PARANOIA, test x$enable_cdio_paranoia = xyes) - -dnl ---------------------------------- libmms --------------------------------- -MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4], +MPD_ENABLE_AUTO_PKG(mms, MMS, [libmms >= 0.4], [libmms mms:// protocol support], [libmms not found]) -if test x$enable_mms = xyes; then - AC_DEFINE(ENABLE_MMS, 1, - [Define when libmms is used for the MMS protocol]) -fi -AM_CONDITIONAL(ENABLE_MMS, test x$enable_mms = xyes) + +dnl --------------------------------------------------------------------------- +dnl Playlist Plugins +dnl --------------------------------------------------------------------------- + +MPD_ARG_ENABLE(cue, CUE, [CUE sheet parser], yes) dnl --------------------------------------------------------------------------- dnl Neighbor Plugins @@ -1028,24 +758,18 @@ if test x$enable_neighbor_plugins = xauto; then fi fi -if test x$enable_neighbor_plugins = xyes; then - AC_DEFINE(ENABLE_NEIGHBOR_PLUGINS, 1, - [Define to enable support for neighbor discovery]) -fi -AM_CONDITIONAL(ENABLE_NEIGHBOR_PLUGINS, test x$enable_neighbor_plugins = xyes) +MPD_DEFINE_CONDITIONAL(enable_neighbor_plugins, ENABLE_NEIGHBOR_PLUGINS, + [neighbor discovery]) dnl --------------------------------------------------------------------------- dnl Archive Plugins dnl --------------------------------------------------------------------------- dnl --------------------------------- iso9660 --------------------------------- -MPD_AUTO_PKG(iso9660, ISO9660, [libiso9660], - [libiso9660 archive library], [libiso9660 not found]) +MPD_ENABLE_AUTO_PKG(iso9660, ISO9660, [libiso9660], + [libiso9660 archive plugin], [libiso9660 not found]) -AM_CONDITIONAL(HAVE_ISO9660, test x$enable_iso9660 = xyes) if test x$enable_iso9660 = xyes; then - AC_DEFINE(HAVE_ISO9660, 1, [Define to have ISO9660 archive support]) - AC_PATH_PROG(MKISOFS, mkisofs, no) else MKISOFS="no" @@ -1055,23 +779,15 @@ AM_CONDITIONAL(ENABLE_ISO9660_TEST, test x$MKISOFS != xno) dnl ---------------------------------- zlib --------------------------------- -MPD_AUTO_PKG(zlib, ZLIB, [zlib], +MPD_ENABLE_AUTO_PKG(zlib, ZLIB, [zlib], [zlib support], [zlib not found]) -AM_CONDITIONAL(HAVE_ZLIB, test x$enable_zlib = xyes) -if test x$enable_zlib = xyes; then - AC_DEFINE(HAVE_ZLIB, 1, [Define to enable zlib support]) -fi - dnl ---------------------------------- libbz2 --------------------------------- -MPD_AUTO_LIB(bzip2, BZ2, bz2, BZ2_bzDecompressInit, [-lbz2], [], - [bzip2], [libbz2 not found]) +MPD_ENABLE_AUTO_LIB(bzip2, BZ2, bz2, BZ2_bzDecompressInit, [-lbz2], [], + [bzip2 archive plugin], [libbz2 not found]) -AM_CONDITIONAL(HAVE_BZ2, test x$enable_bzip2 = xyes) if test x$enable_bzip2 = xyes; then - AC_DEFINE(HAVE_BZ2, 1, [Define to have bz2 archive support]) - AC_PATH_PROG(BZIP2, bzip2, no) else BZIP2="no" @@ -1090,21 +806,16 @@ if test x$enable_expat = xno; then fi fi -MPD_AUTO_PKG(upnp, UPNP, [libupnp], - [UPnP client support], [libupnp not found]) -if test x$enable_upnp = xyes; then - AC_DEFINE(HAVE_LIBUPNP, 1, [Define when libupnp is used]) -fi -AM_CONDITIONAL(HAVE_LIBUPNP, test x$enable_upnp = xyes) +MPD_ENABLE_AUTO_PKG_DEPENDS(upnp, UPNP, [libupnp], + [UPnP client support], [libupnp not found], [], + [enable_database], [Cannot use --enable-upnp with --disable-database]) dnl --------------------------------- libzzip --------------------------------- -MPD_AUTO_PKG(zzip, ZZIP, [zziplib >= 0.13], - [libzzip archive library], [libzzip not found]) +MPD_ENABLE_AUTO_PKG(zzip, ZZIP, [zziplib >= 0.13], + [libzzip archive library], [libzzip not found], + [no]) -AM_CONDITIONAL(HAVE_ZZIP, test x$enable_zzip = xyes) if test x$enable_zzip = xyes; then - AC_DEFINE(HAVE_ZZIP, 1, [Define to have zip archive support]) - AC_PATH_PROG(ZIP, zip, no) else ZIP="no" @@ -1118,96 +829,59 @@ if test x$enable_zzip = xyes || test x$enable_iso9660 = xyes; then enable_archive=yes - AC_DEFINE(ENABLE_ARCHIVE, 1, [The archive API is available]) else enable_archive=no fi -AM_CONDITIONAL(ENABLE_ARCHIVE, test x$enable_archive = xyes) +MPD_DEFINE_CONDITIONAL(enable_archive, ENABLE_ARCHIVE, [the archive API]) dnl --------------------------------------------------------------------------- dnl Decoder Plugins dnl --------------------------------------------------------------------------- dnl -------------------------------- libadplug -------------------------------- -MPD_AUTO_PKG(adplug, ADPLUG, [adplug], +MPD_ENABLE_AUTO_PKG(adplug, ADPLUG, [adplug], [AdPlug decoder plugin], [libadplug not found]) -if test x$enable_adplug = xyes; then - AC_DEFINE(HAVE_ADPLUG, 1, [Define to use libadplug]) -fi -AM_CONDITIONAL(HAVE_ADPLUG, test x$enable_adplug = xyes) dnl -------------------------------- audiofile -------------------------------- -MPD_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.3], +MPD_ENABLE_AUTO_PKG(audiofile, AUDIOFILE, [audiofile >= 0.3], [audiofile decoder plugin], [libaudiofile not found]) -AM_CONDITIONAL(HAVE_AUDIOFILE, test x$enable_audiofile = xyes) -if test x$enable_audiofile = xyes; then - AC_DEFINE(HAVE_AUDIOFILE, 1, [Define for audiofile support]) -fi dnl ----------------------------------- DSD ----------------------------------- -if test x$enable_dsd = xyes; then - AC_DEFINE(ENABLE_DSD, 1, [Define for the DSD decoder]) -fi - -AM_CONDITIONAL(ENABLE_DSD, test x$enable_dsd = xyes) +MPD_DEFINE_CONDITIONAL(enable_dsd, ENABLE_DSD, [DSD decoder]) dnl ----------------------------------- FAAD ---------------------------------- -AM_PATH_FAAD() - -AM_CONDITIONAL(HAVE_FAAD, test x$enable_aac = xyes) +MPD_ENABLE_AUTO_LIB(aac, FAAD, faad, NeAACDecOpen, [-lfaad], [], + [FAAD decoder plugin], [libfaad not found]) dnl ---------------------------------- ffmpeg --------------------------------- -MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 53.17 libavcodec >= 53.25 libavutil >= 51.17], +MPD_ENABLE_AUTO_PKG(ffmpeg, FFMPEG, + [libavformat >= 53.17 libavcodec >= 53.25 libavutil >= 51.17], [ffmpeg decoder library], [libavformat+libavcodec+libavutil not found]) -if test x$enable_ffmpeg = xyes; then - AC_DEFINE(HAVE_FFMPEG, 1, [Define for FFMPEG support]) -fi - -AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes) - dnl ----------------------------------- FLAC ---------------------------------- -MPD_AUTO_PKG(flac, FLAC, [flac >= 1.2], +MPD_ENABLE_AUTO_PKG(flac, FLAC, [flac >= 1.2], [FLAC decoder], [libFLAC not found]) -if test x$enable_flac = xyes; then - AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]) -fi - -AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes) - enable_flac_encoder=$enable_flac dnl -------------------------------- FluidSynth ------------------------------- -MPD_AUTO_PKG(fluidsynth, FLUIDSYNTH, [fluidsynth >= 1.1], - [fluidsynth decoder], [fluidsynth not found]) - -if test x$enable_fluidsynth = xyes; then - AC_DEFINE(ENABLE_FLUIDSYNTH, 1, [Define for fluidsynth support]) -fi - -AM_CONDITIONAL(ENABLE_FLUIDSYNTH, test x$enable_fluidsynth = xyes) +MPD_ENABLE_AUTO_PKG(fluidsynth, FLUIDSYNTH, [fluidsynth >= 1.1], + [fluidsynth MIDI decoder plugin], [fluidsynth not found]) dnl ---------------------------------- libgme --------------------------------- -MPD_AUTO_PKG_LIB(gme, GME, [libgme], gme, gme_open_file, [-lgme -lstdc++], [], - [gme decoder plugin], [libgme not found]) -AM_CONDITIONAL(HAVE_GME, test x$enable_gme = xyes) -if test x$enable_gme = xyes; then - AC_DEFINE(HAVE_GME, 1, [Define for gme support]) -fi + +MPD_ENABLE_AUTO_PKG_LIB(gme, GME, [libgme], + gme, gme_open_file, [-lgme], [], + [Game Music Emulator decoder plugin], [libgme not found]) dnl ---------------------------------- libmad --------------------------------- -MPD_AUTO_PKG_LIB(mad, MAD, [mad], +MPD_ENABLE_AUTO_PKG_LIB(mad, MAD, [mad], mad, mad_stream_init, [-lmad], [], [libmad MP3 decoder plugin], [libmad not found]) -if test x$enable_mad = xyes; then - AC_DEFINE(HAVE_MAD, 1, [Define to use libmad]) -fi -AM_CONDITIONAL(HAVE_MAD, test x$enable_mad = xyes) enable_shout2="$enable_shout" MPD_AUTO_PKG(shout, SHOUT, [shout], @@ -1217,12 +891,8 @@ if test x$enable_shout = xyes && test x$enable_shout2 = xauto; then fi dnl -------------------------------- libmpg123 -------------------------------- -MPD_AUTO_PKG(mpg123, MPG123, [libmpg123], +MPD_ENABLE_AUTO_PKG(mpg123, MPG123, [libmpg123], [libmpg123 decoder plugin], [libmpg123 not found]) -if test x$enable_mpg123 = xyes; then - AC_DEFINE(HAVE_MPG123, 1, [Define to use libmpg123]) -fi -AM_CONDITIONAL(HAVE_MPG123, test x$enable_mpg123 = xyes) dnl -------------------------------- libmikmod -------------------------------- if test x$enable_mikmod = xyes; then @@ -1239,72 +909,57 @@ fi AM_CONDITIONAL(ENABLE_MIKMOD_DECODER, test x$enable_mikmod = xyes) dnl -------------------------------- libmodplug ------------------------------- -MPD_AUTO_PKG(modplug, MODPLUG, [libmodplug], +MPD_ENABLE_AUTO_PKG(modplug, MODPLUG, [libmodplug], [modplug decoder plugin], [libmodplug not found]) -if test x$enable_modplug = xyes; then - AC_DEFINE(HAVE_MODPLUG, 1, [Define for modplug support]) -fi -AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes) - dnl -------------------------------- libopus ---------------------------------- -MPD_AUTO_PKG(opus, OPUS, [opus ogg], +MPD_ENABLE_AUTO_PKG(opus, OPUS, [opus ogg], [opus decoder plugin], [libopus not found]) -if test x$enable_opus = xyes; then - AC_DEFINE(HAVE_OPUS, 1, [Define to use libopus]) -fi -AM_CONDITIONAL(HAVE_OPUS, test x$enable_opus = xyes) dnl -------------------------------- libsndfile ------------------------------- dnl See above test, which may disable this. -MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile], +MPD_ENABLE_AUTO_PKG(sndfile, SNDFILE, [sndfile], [libsndfile decoder plugin], [libsndfile not found]) -if test x$enable_sndfile = xyes; then - AC_DEFINE(ENABLE_SNDFILE, 1, [Define to enable the sndfile decoder plugin]) -fi -AM_CONDITIONAL(ENABLE_SNDFILE, test x$enable_sndfile = xyes) - dnl --------------------------------- musepack -------------------------------- -MPD_AUTO_LIB(mpc, MPCDEC, mpcdec, mpc_demux_init, [-lmpcdec], [], - [mpcdec], [libmpcdec not found]) -if test x$enable_mpc = xyes; then - AC_DEFINE(HAVE_MPCDEC, 1, [Define to use libmpcdec for MPC decoding]) -fi -AM_CONDITIONAL(HAVE_MPCDEC, test x$enable_mpc = xyes) +MPD_ENABLE_AUTO_LIB(mpc, MPCDEC, mpcdec, mpc_demux_init, [-lmpcdec], [], + [Musepack decoder plugin], [libmpcdec not found]) dnl -------------------------------- Ogg Tremor ------------------------------- + +AC_ARG_WITH(tremor, + AS_HELP_STRING([--with-tremor=PFX], + [use Tremor (vorbisidec) integer Ogg Vorbis decoder (with optional prefix)]),, + with_tremor=no) + +AC_ARG_VAR([TREMOR_CFLAGS], [C compiler flags for Tremor]) +AC_ARG_VAR([TREMOR_LIBS], [linker flags for Tremor]) + if test x$with_tremor = xyes || test x$with_tremor = xno; then enable_tremor="$with_tremor" + tremor_prefix="" else tremor_prefix="$with_tremor" enable_tremor=yes fi if test x$enable_tremor = xyes; then - if test "x$tremor_libraries" != "x" ; then - TREMOR_LIBS="-L$tremor_libraries" - elif test "x$tremor_prefix" != "x" ; then - TREMOR_LIBS="-L$tremor_prefix/lib" - fi - TREMOR_LIBS="$TREMOR_LIBS -lvorbisidec" - if test "x$tremor_includes" != "x" ; then - TREMOR_CFLAGS="-I$tremor_includes" - elif test "x$tremor_prefix" != "x" ; then + if test x$TREMOR_CFLAGS = x && test x$tremor_prefix != x; then TREMOR_CFLAGS="-I$tremor_prefix/include" fi - ac_save_CFLAGS="$CFLAGS" - ac_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $TREMOR_CFLAGS" - LIBS="$LIBS $TREMOR_LIBS" - AC_CHECK_LIB(vorbisidec,ov_read,,enable_tremor=no; - AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support])) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" -fi + if test x$TREMOR_LIBS = x; then + TREMOR_LIBS="-lvorbisidec" + + if test x$tremor_prefix != x; then + TREMOR_LIBS="-L$tremor_prefix/lib $TREMOR_LIBS" + fi + fi + + MPD_WITH_LIBRARY([TREMOR], + [AC_CHECK_FUNC([ov_read],, + [AC_MSG_ERROR([libvorbisidec not found])])]) -if test x$enable_tremor = xyes; then AC_DEFINE(HAVE_TREMOR,1, [Define to use tremor (libvorbisidec) for ogg support]) AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]) @@ -1313,9 +968,6 @@ else TREMOR_LIBS= fi -AC_SUBST(TREMOR_CFLAGS) -AC_SUBST(TREMOR_LIBS) - dnl -------------------------------- Ogg Vorbis ------------------------------- if test x$enable_tremor = xyes; then @@ -1370,21 +1022,12 @@ fi AM_CONDITIONAL(ENABLE_SIDPLAY, test x$enable_sidplay = xyes) dnl --------------------------------- wavpack --------------------------------- -MPD_AUTO_PKG(wavpack, WAVPACK, [wavpack], +MPD_ENABLE_AUTO_PKG(wavpack, WAVPACK, [wavpack], [WavPack decoder plugin], [libwavpack not found]) -AM_CONDITIONAL(HAVE_WAVPACK, test x$enable_wavpack = xyes) -if test x$enable_wavpack = xyes; then - AC_DEFINE([HAVE_WAVPACK], 1, [Define to enable WavPack support]) -fi dnl --------------------------------- WildMidi -------------------------------- -MPD_AUTO_LIB(wildmidi, WILDMIDI, WildMidi, WildMidi_Init, [-lWildMidi], [], - [wildmidi], [libwildmidi not found]) -if test x$enable_wildmidi = xyes; then - AC_DEFINE(ENABLE_WILDMIDI, 1, [Define for wildmidi support]) -fi - -AM_CONDITIONAL(ENABLE_WILDMIDI, test x$enable_wildmidi = xyes) +MPD_ENABLE_AUTO_LIB(wildmidi, WILDMIDI, WildMidi, WildMidi_Init, [-lWildMidi], [], + [WildMidi decoder plugin], [libwildmidi not found]) dnl ------------------------ Post Decoder Plugins Tests ----------------------- @@ -1419,59 +1062,30 @@ else fi dnl ------------------------------- FLAC Encoder ------------------------------ -if test x$enable_flac_encoder = xyes; then - AC_DEFINE(ENABLE_FLAC_ENCODER, 1, - [Define to enable the FLAC encoder plugin]) -fi -AM_CONDITIONAL(ENABLE_FLAC_ENCODER, test x$enable_flac_encoder = xyes) +MPD_DEFINE_CONDITIONAL(enable_flac_encoder, ENABLE_FLAC_ENCODER, + [FLAC encoder plugin]) dnl ------------------------------- Shine Encoder ------------------------------ -MPD_AUTO_PKG(shine_encoder, SHINE, [shine >= 3.1], +MPD_ENABLE_AUTO_PKG(shine_encoder, SHINE, [shine >= 3.1], [shine encoder], [libshine not found]) -if test x$enable_shine_encoder = xyes; then - AC_DEFINE(ENABLE_SHINE_ENCODER, 1, - [Define to enable the shine encoder plugin]) -fi -AM_CONDITIONAL(ENABLE_SHINE_ENCODER, test x$enable_shine_encoder = xyes) - dnl ---------------------------- Ogg Vorbis Encoder --------------------------- -MPD_AUTO_PKG(vorbis_encoder, VORBISENC, [vorbisenc vorbis ogg], +MPD_ENABLE_AUTO_PKG(vorbis_encoder, VORBISENC, [vorbisenc vorbis ogg], [Ogg Vorbis encoder], [libvorbisenc not found]) -if test x$enable_vorbis_encoder = xyes; then - AC_DEFINE(ENABLE_VORBIS_ENCODER, 1, - [Define to enable the vorbis encoder plugin]) -fi -AM_CONDITIONAL(ENABLE_VORBIS_ENCODER, test x$enable_vorbis_encoder = xyes) - dnl ------------------------------- LAME Encoder ------------------------------ -MPD_AUTO_LIB(lame_encoder, LAME, mp3lame, lame_init, [-lmp3lame], [], - [libmp3lame], [libmp3lame not found]) -if test x$enable_lame_encoder = xyes; then - AC_DEFINE(ENABLE_LAME_ENCODER, 1, - [Define to enable the lame encoder plugin]) -fi -AM_CONDITIONAL(ENABLE_LAME_ENCODER, test x$enable_lame_encoder = xyes) +MPD_ENABLE_AUTO_LIB(lame_encoder, LAME, mp3lame, lame_init, [-lmp3lame], [], + [LAME encoder plugin], [libmp3lame not found]) dnl ----------------------------- TwoLAME Encoder ----------------------------- -MPD_AUTO_PKG(twolame_encoder, TWOLAME, [twolame], - [TwoLAME encoder], [libtwolame not found]) - -if test x$enable_twolame_encoder = xyes; then - AC_DEFINE(ENABLE_TWOLAME_ENCODER, 1, - [Define to enable the TwoLAME encoder plugin]) -fi -AM_CONDITIONAL(ENABLE_TWOLAME_ENCODER, test x$enable_twolame_encoder = xyes) +MPD_ENABLE_AUTO_PKG(twolame_encoder, TWOLAME, [twolame], + [TwoLAME encoder plugin], [libtwolame not found]) dnl ------------------------------- WAVE Encoder ------------------------------ -AM_CONDITIONAL(ENABLE_WAVE_ENCODER, test x$enable_wave_encoder = xyes) -if test x$enable_wave_encoder = xyes; then - AC_DEFINE(ENABLE_WAVE_ENCODER, 1, - [Define to enable the PCM wave encoder plugin]) -fi +MPD_DEFINE_CONDITIONAL(enable_wave_encoder, ENABLE_WAVE_ENCODER, + [PCM wave encoder plugin]) dnl --------------------------- encoder plugins test -------------------------- if test x$enable_vorbis_encoder != xno || @@ -1492,11 +1106,9 @@ else fi fi -if test x$enable_encoder = xyes; then - AC_DEFINE(ENABLE_ENCODER, 1, - [Define to enable the encoder plugins]) -fi -AM_CONDITIONAL(ENABLE_ENCODER, test x$enable_encoder = xyes) +MPD_DEFINE_CONDITIONAL(enable_encoder, ENABLE_ENCODER, + [the encoder plugins]) + AM_CONDITIONAL(HAVE_OGG_ENCODER, test x$enable_vorbis_encoder = xyes || test x$enable_opus = xyes) dnl --------------------------------------------------------------------------- @@ -1504,34 +1116,36 @@ dnl Audio Output Plugins dnl --------------------------------------------------------------------------- dnl ----------------------------------- ALSA ---------------------------------- -MPD_AUTO_PKG(alsa, ALSA, [alsa >= 0.9.0], - [ALSA output plugin], [libasound not found]) - -if test x$enable_alsa = xyes; then - AC_DEFINE(HAVE_ALSA, 1, [Define to enable ALSA support]) -fi - -AM_CONDITIONAL(HAVE_ALSA, test x$enable_alsa = xyes) +MPD_ENABLE_AUTO_PKG(alsa, ALSA, [alsa >= 0.9.0], + [ALSA output plugin], [libasound not found], + [$linux_auto]) dnl ----------------------------------- ROAR ---------------------------------- -MPD_AUTO_PKG(roar, ROAR, [libroar >= 0.4.0], - [ROAR output plugin], [libroar not found]) - -if test x$enable_roar = xyes; then - AC_DEFINE(HAVE_ROAR, 1, [Define to enable ROAR support]) -fi - -AM_CONDITIONAL(HAVE_ROAR, test x$enable_roar = xyes) +MPD_ENABLE_AUTO_PKG(roar, ROAR, [libroar >= 0.4.0], + [RoarAudio output plugin], [libroar not found]) dnl ----------------------------------- FIFO ---------------------------------- if test x$enable_fifo = xyes; then AC_CHECK_FUNC([mkfifo], - [enable_fifo=yes;AC_DEFINE([HAVE_FIFO], 1, - [Define to enable support for writing audio to a FIFO])], + [enable_fifo=yes], [enable_fifo=no;AC_MSG_WARN([mkfifo not found -- disabling support for writing audio to a FIFO])]) fi -AM_CONDITIONAL(HAVE_FIFO, test x$enable_fifo = xyes) +MPD_DEFINE_CONDITIONAL(enable_fifo, HAVE_FIFO, + [support for writing audio to a FIFO]) + +dnl ----------------------------------- Haiku --------------------------------- +if test x$enable_haiku = xauto; then + AC_CHECK_HEADER(media/MediaDefs.h, + [enable_haiku=yes], + [enable_haiku=no]) +fi +if test x$enable_haiku = xyes; then + AC_DEFINE(HAVE_HAIKU,1,[Define for compiling Haiku support]) + LIBS="$LIBS -lbe -lmedia" +fi + +AM_CONDITIONAL(HAVE_HAIKU, test x$enable_haiku = xyes) dnl ------------------------------- HTTPD Output ------------------------------ if test x$enable_httpd_output = xauto; then @@ -1545,17 +1159,12 @@ if test x$enable_httpd_output = xauto; then fi fi -if test x$enable_httpd_output = xyes; then - AC_DEFINE(ENABLE_HTTPD_OUTPUT, 1, [Define to enable the HTTP server output]) -fi -AM_CONDITIONAL(ENABLE_HTTPD_OUTPUT, test x$enable_httpd_output = xyes) +MPD_DEFINE_CONDITIONAL(enable_httpd_output, ENABLE_HTTPD_OUTPUT, + [the HTTP server output]) dnl ----------------------------------- JACK ---------------------------------- -MPD_AUTO_PKG(jack, JACK, [jack >= 0.100], +MPD_ENABLE_AUTO_PKG(jack, JACK, [jack >= 0.100], [JACK output plugin], [libjack not found]) -if test x$enable_jack = xyes; then - AC_DEFINE([HAVE_JACK], 1, [Define to enable JACK support]) -fi if test x$enable_jack = xyes; then # check whether jack_set_info_function() is available @@ -1567,16 +1176,9 @@ if test x$enable_jack = xyes; then LIBS=$old_LIBS fi -AM_CONDITIONAL(HAVE_JACK, test x$enable_jack = xyes) - dnl ---------------------------------- libao ---------------------------------- -MPD_AUTO_PKG(ao, AO, [ao], +MPD_ENABLE_AUTO_PKG(ao, AO, [ao], [libao output plugin], [libao not found]) -if test x$enable_ao = xyes; then - AC_DEFINE(HAVE_AO, 1, [Define to play with ao]) -fi - -AM_CONDITIONAL(HAVE_AO, test x$enable_ao = xyes) dnl ---------------------------------- OpenAL --------------------------------- AC_SUBST(OPENAL_CFLAGS,"") @@ -1598,21 +1200,17 @@ else [OpenAL output plugin], [OpenAL not found]) fi -if test x$enable_openal = xyes; then - AC_DEFINE(HAVE_OPENAL, 1, [Define for OpenAL support]) -fi - -AM_CONDITIONAL(HAVE_OPENAL, test x$enable_openal = xyes) +MPD_DEFINE_CONDITIONAL(enable_openal, HAVE_OPENAL, [OpenAL support]) dnl ---------------------------- Open Sound System ---------------------------- if test x$enable_oss = xyes; then AC_CHECK_HEADER(sys/soundcard.h, - [enable_oss=yes;AC_DEFINE(HAVE_OSS,1,[Define to enable OSS])], + [enable_oss=yes], [AC_MSG_WARN(Soundcard headers not found -- disabling OSS support); enable_oss=no]) fi -AM_CONDITIONAL(HAVE_OSS, test x$enable_oss = xyes) +MPD_DEFINE_CONDITIONAL(enable_oss, HAVE_OSS, [Open Sound System]) dnl ----------------------------------- OSX ----------------------------------- if test x$enable_osx = xyes; then @@ -1623,21 +1221,12 @@ fi AM_CONDITIONAL(HAVE_OSX, test x$enable_osx = xyes) dnl ------------------------------- Pipe Output ------------------------------- -if test x$enable_pipe_output = xyes; then - AC_DEFINE([ENABLE_PIPE_OUTPUT], 1, - [Define to enable support for writing audio to a pipe]) -fi -AM_CONDITIONAL(ENABLE_PIPE_OUTPUT, test x$enable_pipe_output = xyes) +MPD_DEFINE_CONDITIONAL(enable_pipe_output, ENABLE_PIPE_OUTPUT, + [support for writing audio to a pipe]) dnl -------------------------------- PulseAudio ------------------------------- -MPD_AUTO_PKG(pulse, PULSE, [libpulse >= 0.9.16], +MPD_ENABLE_AUTO_PKG(pulse, PULSE, [libpulse >= 0.9.16], [PulseAudio output plugin], [libpulse not found]) -if test x$enable_pulse = xyes; then - AC_DEFINE([HAVE_PULSE], 1, - [Define to enable PulseAudio support]) -fi - -AM_CONDITIONAL(HAVE_PULSE, test x$enable_pulse = xyes) dnl --------------------------------- Recorder -------------------------------- if test x$enable_recorder_output = xauto; then @@ -1651,10 +1240,8 @@ if test x$enable_recorder_output = xauto; then fi fi -if test x$enable_recorder_output = xyes; then - AC_DEFINE(ENABLE_RECORDER_OUTPUT, 1, [Define to enable the recorder output]) -fi -AM_CONDITIONAL(ENABLE_RECORDER_OUTPUT, test x$enable_recorder_output = xyes) +MPD_DEFINE_CONDITIONAL(enable_recorder_output, ENABLE_RECORDER_OUTPUT, + [the recorder output]) dnl -------------------------------- SHOUTcast -------------------------------- if test x$enable_shout = xauto; then @@ -1668,18 +1255,13 @@ if test x$enable_shout = xauto; then fi fi -if test x$enable_shout = xyes; then - AC_DEFINE(HAVE_SHOUT, 1, [Define to enable the shoutcast output]) -fi -AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes) +MPD_DEFINE_CONDITIONAL(enable_shout, HAVE_SHOUT, + [shoutcast output]) dnl --------------------------------- Solaris --------------------------------- -if test x$enable_solaris_output = xyes; then - AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support]) -fi - -AM_CONDITIONAL(ENABLE_SOLARIS_OUTPUT, test x$enable_solaris_output = xyes) +MPD_DEFINE_CONDITIONAL(enable_solaris_output, ENABLE_SOLARIS_OUTPUT, + [Solaris /dev/audio support]) dnl --------------------------------- WinMM --------------------------------- @@ -1858,6 +1440,7 @@ printf '\nPlayback support:\n\t' results(alsa,ALSA) results(fifo,FIFO) results(recorder_output,[File Recorder]) +results(haiku,[Haiku]) results(httpd_output,[HTTP Daemon]) results(jack,[JACK]) printf '\n\t' diff --git a/doc/doxygen.conf.in b/doc/doxygen.conf.in index 1d9d0ca7c..440df06bf 100644 --- a/doc/doxygen.conf.in +++ b/doc/doxygen.conf.in @@ -1,96 +1,129 @@ -# Doxyfile 1.5.6 +# Doxyfile 1.8.8 # This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project +# doxygen (www.doxygen.org) for a project. # -# All text after a hash (#) is considered a comment and will be ignored +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. # The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# http://www.gnu.org/software/libiconv for the list of possible encodings. +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. PROJECT_NAME = MPD -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. -PROJECT_NUMBER = @VERSION@ +PROJECT_BRIEF = -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. OUTPUT_DIRECTORY = doc/api -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. CREATE_SUBDIRS = NO +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, -# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, -# Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, -# and Ukrainian. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. +# The default value is: YES. REPEAT_BRIEF = YES -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief +# doxygen will generate a detailed section even if there is only a brief # description. +# The default value is: NO. ALWAYS_DETAILED_SEC = NO @@ -98,148 +131,207 @@ ALWAYS_DETAILED_SEC = NO # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. +# The default value is: NO. INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. FULL_PATH_NAMES = YES -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = @abs_top_srcdir@/src/ -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. STRIP_FROM_INC_PATH = -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. SHORT_NAMES = NO -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. JAVADOC_AUTOBRIEF = YES -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. QT_AUTOBRIEF = NO -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. SEPARATE_MEMBER_PAGES = NO -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 8 -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. ALIASES = -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. +# The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. +# The default value is: NO. CPP_CLI_SUPPORT = NO -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. SIP_SUPPORT = NO -# For Microsoft's IDL there are propget and propput attributes to indicate getter -# and setter methods for a property. Setting this option to YES (the default) -# will make doxygen to replace the get and set methods by a property in the -# documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. IDL_PROPERTY_SUPPORT = YES @@ -247,335 +339,471 @@ IDL_PROPERTY_SUPPORT = YES # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. +# The default value is: NO. DISTRIBUTE_GROUP_DOC = NO -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. SUBGROUPING = YES -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. TYPEDEF_HIDES_STRUCT = NO +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. EXTRACT_ALL = YES -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. EXTRACT_STATIC = YES -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespace are hidden. +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. EXTRACT_ANON_NSPACES = NO -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_MEMBERS = NO -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. HIDE_UNDOC_CLASSES = NO -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. HIDE_IN_BODY_DOCS = NO -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. +# The default value is: system dependent. CASE_SENSE_NAMES = YES -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. HIDE_SCOPE_NAMES = NO -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. SHOW_INCLUDE_FILES = YES -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. INLINE_INFO = YES -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. SORT_MEMBER_DOCS = YES -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. SORT_BRIEF_DOCS = NO -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. SORT_GROUP_NAMES = NO -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. SORT_BY_SCOPE_NAME = NO -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. GENERATE_DEPRECATEDLIST= YES -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. ENABLED_SECTIONS = -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. SHOW_USED_FILES = YES -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. SHOW_FILES = YES -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via -# popen()) the command <command> <input-file>, where <command> is the value of -# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. FILE_VERSION_FILTER = +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. WARNINGS = YES -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. WARN_IF_DOC_ERROR = YES -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. WARN_NO_PARAMDOC = NO -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. -INPUT = @abs_top_srcdir@/src/ +INPUT = @abs_top_srcdir@/src/ # This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for -# the list of possible encodings. +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. -FILE_PATTERNS = *.h +FILE_PATTERNS = *.h \ + *.hxx -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. -RECURSIVE = NO +RECURSIVE = YES -# The EXCLUDE tag can be used to specify files and/or directories that should +# The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. EXCLUDE = -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded # from the input. +# The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = @@ -584,528 +812,1108 @@ EXCLUDE_PATTERNS = # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. EXAMPLE_RECURSIVE = NO -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command <filter> <input-file>, where <filter> -# is the value of the INPUT_FILTER tag, and <input-file> is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. FILTER_SOURCE_FILES = NO +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. SOURCE_BROWSER = YES -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. INLINE_SOURCES = NO -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. STRIP_CODE_COMMENTS = YES -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. REFERENCED_BY_RELATION = NO -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. REFERENCES_RELATION = NO -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. REFERENCES_LINK_SOURCE = YES -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. ALPHABETICAL_INDEX = NO -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. GENERATE_HTML = YES -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a # standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra stylesheet files is of importance (e.g. the last +# stylesheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_HTMLHELP = NO +HTML_DYNAMIC_SECTIONS = NO -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. For this to work a browser that supports -# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox -# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. -HTML_DYNAMIC_SECTIONS = NO +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be # written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to FRAME, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. Other possible values -# for this tag are: HIERARCHIES, which will generate the Groups, Directories, -# and Class Hiererachy pages using a tree view instead of an ordered list; -# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which -# disables this behavior completely. For backwards compatibility with previous -# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE -# respectively. +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NONE -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer ( doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer ( doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + #--------------------------------------------------------------------------- -# configuration options related to the LaTeX output +# Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. +# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# The default value is: YES. GENERATE_LATEX = NO -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. +# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. COMPACT_LATEX = NO -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. PAPER_TYPE = a4wide -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. To get the times font for +# instance you can specify +# EXTRA_PACKAGES=times +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, +# for the replacement values of the other commands the user is refered to +# HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES to get a # higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. USE_PDFLATEX = YES -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BATCHMODE = NO -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HIDE_INDICES = NO +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + #--------------------------------------------------------------------------- -# configuration options related to the RTF output +# Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. +# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. GENERATE_RTF = NO -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. +# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. COMPACT_RTF = NO -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. RTF_STYLESHEET_FILE = -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- -# configuration options related to the man page output +# Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages +# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# classes and files. +# The default value is: NO. GENERATE_MAN = NO -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. MAN_OUTPUT = man -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. MAN_EXTENSION = .3 -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. MAN_LINKS = NO #--------------------------------------------------------------------------- -# configuration options related to the XML output +# Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. +# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. GENERATE_XML = NO -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. XML_OUTPUT = xml -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. +# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES -XML_SCHEMA = +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. +# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. -XML_DTD = +GENERATE_DOCBOOK = NO -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. -XML_PROGRAMLISTING = YES +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output +# Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen +# Definitions (see http://autogen.sf.net) file that captures the structure of +# the code including all documentation. Note that this feature is still +# experimental and incomplete at the moment. +# The default value is: NO. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- -# configuration options related to the Perl module output +# Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. +# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. +# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. PERLMOD_PRETTY = YES -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. PERLMOD_MAKEVAR_PREFIX = @@ -1113,108 +1921,130 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. +# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. +# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names +# in the source code. If set to NO only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. MACRO_EXPANSION = YES -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. EXPAND_ONLY_PREDEF = YES -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. +# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. INCLUDE_FILE_PATTERNS = -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = GCC_CHECK_VERSION(major,minor)=0 \ + CLANG_OR_GCC_VERSION(major,minor)=0 \ + GCC_OLDER_THAN(major,minor)=0 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. EXPAND_AS_DEFINED = -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::additions related to external references +# Configuration options related to external references #--------------------------------------------------------------------------- -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. TAGFILES = -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. +# If the ALLEXTERNALS tag is set to YES all external class will be listed in the +# class index. If set to NO only the inherited external classes will be listed. +# The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in +# the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. EXTERNAL_GROUPS = YES +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + # The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. PERL_PATH = /usr/bin/perl @@ -1222,196 +2052,304 @@ PERL_PATH = /usr/bin/perl # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more +# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more # powerful graphs. +# The default value is: YES. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: YES. HAVE_DOT = NO -# By default doxygen will write a font called FreeSans.ttf to the output -# directory and reference it in all dot files that doxygen generates. This -# font does not include all possible unicode characters however, so when you need -# these (or just want a differently looking font) you can specify the font name -# using DOT_FONTNAME. You need need to make sure dot is able to find the font, -# which can be done by putting it in a standard location or by setting the -# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory -# containing the font. +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica -DOT_FONTNAME = FreeSans +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. -# By default doxygen will tell dot to use the output directory to look for the -# FreeSans.ttf font (which doxygen will put there itself). If you specify a -# different font using DOT_FONTNAME you can set the path where dot -# can find it using this tag. +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_FONTPATH = -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. COLLABORATION_GRAPH = YES -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. UML_LOOK = NO -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. TEMPLATE_RELATIONS = NO -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. INCLUDE_GRAPH = YES -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. INCLUDED_BY_GRAPH = YES -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. CALL_GRAPH = NO -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. CALLER_GRAPH = NO -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. GRAPHICAL_HIERARCHY = YES -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. +# generated by dot. +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd and svg. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_IMAGE_FORMAT = png -# The tag DOT_PATH can be used to specify the path where the dot tool can be +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. DOTFILE_DIRS = -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. +# This tag requires that the tag HAVE_DOT is set to YES. + +PLANTUML_JAR_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_GRAPH_MAX_NODES = 50 -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is enabled by default, which results in a transparent -# background. Warning: Depending on the platform used, enabling this option -# may lead to badly anti-aliased labels on the edges of a graph (i.e. they -# become hard to read). +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_MULTI_TARGETS = NO -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. +# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5 index 0cd6c313d..23b319d8a 100644 --- a/doc/mpd.conf.5 +++ b/doc/mpd.conf.5 @@ -156,12 +156,6 @@ This specifies the character set used for the filesystem. A list of supported character sets can be obtained by running "iconv \-l". The default is determined from the locale when the db was originally created. .TP -.B id3v1_encoding <charset> -This specifies the character set which ID3v1 tags are encoded in. A list of -supported character sets can be obtained by running "iconv \-l". The default is -to let libid3tag convert them (from ISO-8859-1, as the standard specifies) and -do no additional conversion. -.TP .B gapless_mp3_playback <yes or no> This specifies whether to support gapless playback of MP3s which have the necessary headers. Useful if your MP3s have headers with incorrect diff --git a/doc/mpdconf.example b/doc/mpdconf.example index 4b55f8801..87d3021fb 100644 --- a/doc/mpdconf.example +++ b/doc/mpdconf.example @@ -75,7 +75,8 @@ # # This setting sets the address for the daemon to listen on. Careful attention # should be paid if this is assigned to anything other then the default, any. -# This setting can deny access to control of the daemon. +# This setting can deny access to control of the daemon. Not effective if +# systemd socket activiation is in use. # # For network #bind_to_address "any" @@ -325,13 +326,6 @@ input { # mixer_type "none" # optional #} # -# If MPD has been compiled with libsamplerate support, this setting specifies -# the sample rate converter to use. Possible values can be found in the -# mpd.conf man page or the libsamplerate documentation. By default, this is -# setting is disabled. -# -#samplerate_converter "Fastest Sinc Interpolator" -# ############################################################################### @@ -380,34 +374,4 @@ input { # #filesystem_charset "UTF-8" # -# This setting controls the encoding that ID3v1 tags should be converted from. -# -#id3v1_encoding "ISO-8859-1" -# -############################################################################### - - -# SIDPlay decoder ############################################################# -# -# songlength_database: -# Location of your songlengths file, as distributed with the HVSC. -# The sidplay plugin checks this for matching MD5 fingerprints. -# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq -# -# default_songlength: -# This is the default playing time in seconds for songs not in the -# songlength database, or in case you're not using a database. -# A value of 0 means play indefinitely. -# -# filter: -# Turns the SID filter emulation on or off. -# -#decoder { -# plugin "sidplay" -# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt" -# default_songlength "120" -# filter "true" -#} -# ############################################################################### - diff --git a/doc/protocol.xml b/doc/protocol.xml index 839aa2d03..5427156d0 100644 --- a/doc/protocol.xml +++ b/doc/protocol.xml @@ -238,9 +238,10 @@ "before" the current song in that sequence) will only be scheduled for repeated playback if its priority has become bigger than the priority of the current song. Decreasing the - priority of a song will moved it farther to the end of the + priority of a song will move it farther to the end of the sequence. Changing the priority of the current song has no - effect on the sequence. + effect on the sequence. During playback, a song's priority is + reset to zero. </para> </section> </chapter> @@ -517,6 +518,15 @@ </listitem> <listitem> <para> + <varname>duration</varname>: + <footnote id="since_0_20"><simpara>Introduced with <application>MPD</application> 0.20</simpara></footnote> + <returnvalue> + Duration of the current song in seconds. + </returnvalue> + </para> + </listitem> + <listitem> + <para> <varname>bitrate</varname>: <returnvalue>instantaneous bitrate in kbps</returnvalue> @@ -1465,15 +1475,15 @@ OK <cmdsynopsis> <command>playlistmove</command> <arg choice="req"><replaceable>NAME</replaceable></arg> - <arg choice="req"><replaceable>SONGID</replaceable></arg> - <arg choice="req"><replaceable>SONGPOS</replaceable></arg> + <arg choice="req"><replaceable>FROM</replaceable></arg> + <arg choice="req"><replaceable>TO</replaceable></arg> </cmdsynopsis> </term> <listitem> <para> - Moves <varname>SONGID</varname> in the playlist - <filename>NAME.m3u</filename> to the position - <varname>SONGPOS</varname>. + Moves the song at position <varname>FROM</varname> in + the playlist <filename>NAME.m3u</filename> to the + position <varname>TO</varname>. </para> </listitem> </varlistentry> @@ -1559,6 +1569,7 @@ OK <arg choice="req"><replaceable>TYPE</replaceable></arg> <arg choice="req"><replaceable>WHAT</replaceable></arg> <arg choice="opt"><replaceable>...</replaceable></arg> + <arg choice="opt">window <replaceable>START</replaceable>:<replaceable>END</replaceable></arg> </cmdsynopsis> </term> <listitem> @@ -1603,6 +1614,13 @@ OK <para> <varname>WHAT</varname> is what to find. </para> + + <para> + <varname>window</varname> can be used to query only a + portion of the real response. The parameter is two + zero-based record numbers; a start number and an end + number. + </para> </listitem> </varlistentry> <varlistentry id="command_findadd"> @@ -1754,7 +1772,7 @@ OK <para> Clients that are connected via UNIX domain socket may use this command to read the tags of an arbitrary local - file (URI beginning with "file:///"). + file (URI is an absolute path). </para> </listitem> </varlistentry> @@ -1769,8 +1787,7 @@ OK <para> Read "comments" (i.e. key-value pairs) from the file specified by "URI". This "URI" can be a path relative - to the music directory or a URL in the form - "file:///foo/bar.ogg". + to the music directory or an absolute path. </para> <para> This command may be used to list metadata of remote @@ -1795,6 +1812,7 @@ OK <arg choice="req"><replaceable>TYPE</replaceable></arg> <arg choice="req"><replaceable>WHAT</replaceable></arg> <arg choice="opt"><replaceable>...</replaceable></arg> + <arg choice="opt">window <replaceable>START</replaceable>:<replaceable>END</replaceable></arg> </cmdsynopsis> </term> <listitem> @@ -2119,6 +2137,30 @@ OK </para> </listitem> </varlistentry> + + <varlistentry id="command_sticker_find_equals"> + <term> + <cmdsynopsis> + <command>sticker</command> + <arg choice="plain">find</arg> + <arg choice="req"><replaceable>TYPE</replaceable></arg> + <arg choice="req"><replaceable>URI</replaceable></arg> + <arg choice="req"><replaceable>NAME</replaceable></arg> + <arg choice="plain">=</arg> + <arg choice="req"><replaceable>VALUE</replaceable></arg> + </cmdsynopsis> + </term> + <listitem> + <para> + Searches for stickers with the given value. + </para> + + <para> + Other supported operators are: + "<function><</function>", "<function>></function>" + </para> + </listitem> + </varlistentry> </variablelist> </section> diff --git a/doc/user.xml b/doc/user.xml index 010d2f980..e30b667ca 100644 --- a/doc/user.xml +++ b/doc/user.xml @@ -16,7 +16,7 @@ <application>MPD</application> (Music Player Daemon) is, as the name suggests, a server software allowing you to remotely play your music, handle playlists, deliver music (HTTP streams with - various sub-protocols) and organizze playlists. + various sub-protocols) and organize playlists. </para> <para> @@ -112,7 +112,7 @@ apt-get install g++ \ libsystemd-daemon-dev libwrap0-dev \ libcppunit-dev xmlto \ libboost-dev \ - libglib2.0-dev libicu-dev + libicu-dev </programlisting> <para> @@ -573,7 +573,7 @@ systemctl start mpd.socket</programlisting> <row> <entry> <varname>mixer_type</varname> - <parameter>hardware|software|none</parameter> + <parameter>hardware|software|null|none</parameter> </entry> <entry> Specifies which mixer should be used for this audio @@ -581,7 +581,9 @@ systemctl start mpd.socket</programlisting> linkend="alsa_output">ALSA</link>, <link linkend="oss_output">OSS</link> and <link linkend="pulse_output">PulseAudio</link>), the - software mixer or no mixer + software mixer, the "null" mixer + (<parameter>null</parameter>; allows setting the + volume, but with no effect) or no mixer (<parameter>none</parameter>). By default, the hardware mixer is used for devices which support it, and none for the others. @@ -767,177 +769,10 @@ systemctl start mpd.socket</programlisting> </para> <para> - The following resamplers are available (if enabled at - compile time): + Check the <link linkend="resampler_plugins">resampler plugin + reference</link> for a list of resamplers and how to + configure them. </para> - - <itemizedlist> - <listitem> - <para> - <ulink - url="http://www.mega-nerd.com/SRC/"><application>libsamplerate</application></ulink> - a.k.a. Secret Rabbit Code (SRC). - </para> - </listitem> - - <listitem> - <para> - <ulink - url="http://sourceforge.net/projects/soxr/"><application>libsoxr</application></ulink>, - the SoX Resampler library - </para> - </listitem> - - <listitem> - <para> - internal: low CPU usage, but very poor quality. This is - the fallback if <application>MPD</application> was - compiled without an external resampler. - </para> - </listitem> - </itemizedlist> - - <para> - The setting <varname>samplerate_converter</varname> controls - how <application>MPD</application> shall resample music. - Possible values: - </para> - - <informaltable> - <tgroup cols="2"> - <thead> - <row> - <entry> - Value - </entry> - <entry> - Description - </entry> - </row> - </thead> - <tbody> - <row> - <entry> - "<parameter>internal</parameter>" - </entry> - <entry> - The internal resampler. Low CPU usage, but very - poor quality. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr very high</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Very - High Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr high</parameter>" or - "<parameter>soxr</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "High - Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr medium</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Medium - Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr low</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Low - Quality" setting. - </entry> - </row> - - <row> - <entry> - "<parameter>soxr quick</parameter>" - </entry> - <entry> - Use <application>libsoxr</application> with "Quick" - setting. - </entry> - </row> - - <row> - <entry> - "<parameter>Best Sinc Interpolator</parameter>" or - "<parameter>0</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Band - limited sinc interpolation, best quality, 97dB SNR, - 96% BW. - </entry> - </row> - - <row> - <entry> - "<parameter>Medium Sinc Interpolator</parameter>" or - "<parameter>1</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Band - limited sinc interpolation, medium quality, 97dB - SNR, 90% BW. - </entry> - </row> - - <row> - <entry> - "<parameter>Fastest Sinc Interpolator</parameter>" or - "<parameter>2</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Band - limited sinc interpolation, fastest, 97dB SNR, 80% - BW. - </entry> - </row> - - <row> - <entry> - "<parameter>ZOH Sinc Interpolator</parameter>" or - "<parameter>3</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Zero order - hold interpolator, very fast, very poor quality with - audible distortions. - </entry> - </row> - - <row> - <entry> - "<parameter>Linear Interpolator</parameter>" or - "<parameter>4</parameter>" - </entry> - <entry> - <application>libsamplerate</application>: Linear - interpolator, very fast, poor quality. - </entry> - </row> - </tbody> - </tgroup> - </informaltable> </section> </section> @@ -1294,6 +1129,8 @@ database { To exclude a file from the update, create a file called <filename>.mpdignore</filename> in its parent directory. Each line of that file may contain a list of shell wildcards. + Matching files in the current directory and all subdirectories + are excluded. </para> </section> @@ -1579,6 +1416,19 @@ buffer_size: 16384</programlisting> <application>MPD</application> instance. </entry> </row> + <row> + <entry> + <varname>keepalive</varname> + <parameter>yes|no</parameter> + </entry> + <entry> + Send TCP keepalive packets to the "master" + <application>MPD</application> instance? This option can + help avoid certain firewalls dropping inactive + connections, at the expensive of a very small amount of + additional network traffic. Disabled by default. + </entry> + </row> </tbody> </tgroup> </informaltable> @@ -1736,6 +1586,14 @@ buffer_size: 16384</programlisting> Opens remote files or streams over HTTP. </para> + <para> + Note that unless overridden by the below settings (e.g. by + setting them to a blank value), general curl configuration + from environment variables such as + <varname>http_proxy</varname> or specified in + <filename>~/.curlrc</filename> will be in effect. + </para> + <informaltable> <tgroup cols="2"> <thead> @@ -1937,6 +1795,37 @@ buffer_size: 16384</programlisting> </section> <section> + <title><varname>gme</varname></title> + + <para> + Video game music file emulator based on <ulink + url="https://bitbucket.org/mpyne/game-music-emu/wiki/Home"><application>game-music-emu</application></ulink>. + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry>Setting</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>accuracy</varname> + <parameter>yes|no</parameter> + </entry> + <entry> + Enable more accurate sound emulation. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + + <section> <title><varname>mikmod</varname></title> <para> @@ -2008,6 +1897,64 @@ buffer_size: 16384</programlisting> </informaltable> </section> + <section id="sidplay_decoder"> + <title><varname>sidplay</varname></title> + + <para> + C64 SID decoder based on + <application>libsidplay</application>. + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry>Setting</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>songlength_database</varname> + <parameter>PATH</parameter> + </entry> + <entry> + Location of your songlengths file, as distributed + with the HVSC. The <varname>sidplay</varname> + plugin checks this for matching MD5 fingerprints. + See <ulink + url="http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq">http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq</ulink>. + </entry> + </row> + + <row> + <entry> + <varname>default_songlength</varname> + <parameter>SECONDS</parameter> + </entry> + <entry> + This is the default playing time in seconds for + songs not in the songlength database, or in case + you're not using a database. A value of 0 means + play indefinitely. + </entry> + </row> + + <row> + <entry> + <varname>filter</varname> + <parameter>yes|no</parameter> + </entry> + <entry> + Turns the SID filter emulation on or off. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + <section> <title><varname>wildmidi</varname></title> @@ -2249,6 +2196,251 @@ buffer_size: 16384</programlisting> </section> </section> + <section id="resampler_plugins"> + <title>Resampler plugins</title> + + <para> + The resampler can be configured in a block named + <varname>resampler</varname>, for example: + </para> + + <programlisting>resampler { + plugin "soxr" + quality "very high" +}</programlisting> + + <para> + The following table lists the <varname>resampler</varname> + options valid for all plugins: + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Name + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>plugin</varname> + </entry> + <entry> + The name of the plugin. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + + <section id="internal_resampler"> + <title><varname>internal</varname></title> + + <para> + A resampler built into <application>MPD</application>. Its + quality is very poor, but its CPU usage is low. This is the + fallback if <application>MPD</application> was compiled + without an external resampler. + </para> + </section> + + <section id="libsamplerate_resampler"> + <title><varname>libsamplerate</varname></title> + + <para> + A resampler using <ulink + url="http://www.mega-nerd.com/SRC/"><application>libsamplerate</application></ulink> + a.k.a. Secret Rabbit Code (SRC). + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Name + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>type</varname> + </entry> + <entry> + The interpolator type. See below for a list of + known types. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + + <para> + The following converter types are provided by + <application>libsamplerate</application>: + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Type + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + "<parameter>Best Sinc Interpolator</parameter>" or + "<parameter>0</parameter>" + </entry> + <entry> + Band limited sinc interpolation, best quality, 97dB + SNR, 96% BW. + </entry> + </row> + + <row> + <entry> + "<parameter>Medium Sinc Interpolator</parameter>" or + "<parameter>1</parameter>" + </entry> + <entry> + Band limited sinc interpolation, medium quality, + 97dB SNR, 90% BW. + </entry> + </row> + + <row> + <entry> + "<parameter>Fastest Sinc Interpolator</parameter>" or + "<parameter>2</parameter>" + </entry> + <entry> + Band limited sinc interpolation, fastest, 97dB SNR, + 80% BW. + </entry> + </row> + + <row> + <entry> + "<parameter>ZOH Sinc Interpolator</parameter>" or + "<parameter>3</parameter>" + </entry> + <entry> + Zero order hold interpolator, very fast, very poor + quality with audible distortions. + </entry> + </row> + + <row> + <entry> + "<parameter>Linear Interpolator</parameter>" or + "<parameter>4</parameter>" + </entry> + <entry> + Linear interpolator, very fast, poor quality. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + + <section id="soxr_resampler"> + <title><varname>soxr</varname></title> + + <para> + A resampler using <ulink + url="http://sourceforge.net/projects/soxr/"><application>libsoxr</application></ulink>, + the SoX Resampler library + </para> + + <informaltable> + <tgroup cols="2"> + <thead> + <row> + <entry> + Name + </entry> + <entry> + Description + </entry> + </row> + </thead> + <tbody> + <row> + <entry> + <varname>quality</varname> + </entry> + <entry> + The <application>libsoxr</application> quality + setting. Valid values are: + + <itemizedlist> + <listitem> + <para> + "<parameter>very high</parameter>" + </para> + </listitem> + + <listitem> + <para> + "<parameter>high</parameter>" (the default) + </para> + </listitem> + + <listitem> + <para> + "<parameter>medium</parameter>" + </para> + </listitem> + + <listitem> + <para> + "<parameter>low</parameter>" + </para> + </listitem> + + <listitem> + <para> + "<parameter>quick</parameter>" + </para> + </listitem> + </itemizedlist> + </entry> + </row> + + <row> + <entry> + <varname>threads</varname> + </entry> + <entry> + The number of <application>libsoxr</application> + threads. "0" means "automatic". The default is "1" + which disables multi-threading. + </entry> + </row> + </tbody> + </tgroup> + </informaltable> + </section> + </section> + <section id="output_plugins"> <title>Output plugins</title> @@ -3022,6 +3214,55 @@ buffer_size: 16384</programlisting> Write to this file. </entry> </row> + + <row> + <entry> + <varname>format_path</varname> + <parameter>P</parameter> + </entry> + <entry> + <para> + An alternative to <varname>path</varname> which + provides a format string referring to tag values. + + The special tag <varname>iso8601</varname> emits + the current date and time in <ulink + url="https://en.wikipedia.org/wiki/ISO_8601">ISO8601</ulink> + format (UTC). + Every time a new song starts or a new tag gets + received from a radio station, a new file is + opened. If the format does not render a file + name, nothing is recorded. + </para> + + <para> + A tag name enclosed in percent signs ('%') is + replaced with the tag value. Example: + <parameter>~/.mpd/recorder/%artist% - + %title%.ogg</parameter> + </para> + + <para> + Square brackets can be used to group a substring. + If none of the tags referred in the group can be + found, the whole group is omitted. Example: + <parameter>[~/.mpd/recorder/[%artist% - + ]%title%.ogg]</parameter> (this omits the dash + when no artist tag exists; if title also doesn't + exist, no file is written) + </para> + + <para> + The operators "|" (logical "or") and "&" + (logical "and") can be used to select portions of + the format string depending on the existing tag + values. Example: + <parameter>~/.mpd/recorder/[%title|%name%].ogg</parameter> + (use the "name" tag if no title exists) + </para> + </entry> + </row> + <row> <entry> <varname>encoder</varname> diff --git a/m4/faad.m4 b/m4/faad.m4 deleted file mode 100644 index 9dcb1ccab..000000000 --- a/m4/faad.m4 +++ /dev/null @@ -1,73 +0,0 @@ -AC_DEFUN([AM_PATH_FAAD], -[dnl ## -dnl faad -dnl ## - -AC_ARG_ENABLE(aac, - AS_HELP_STRING([--disable-aac], - [disable AAC support (default: enable)]),, - enable_aac=yes) - -if test x$enable_aac = xyes; then - FAAD_LIBS="-lfaad" - FAAD_CFLAGS="" - - oldcflags=$CFLAGS - oldlibs=$LIBS - oldcppflags=$CPPFLAGS - CFLAGS="$CFLAGS $FAAD_CFLAGS" - LIBS="$LIBS $FAAD_LIBS" - CPPFLAGS=$CFLAGS - AC_CHECK_HEADER(faad.h,,enable_aac=no) - if test x$enable_aac = xyes; then - AC_CHECK_DECL(FAAD2_VERSION,,enable_aac=no,[#include <faad.h>]) - fi - if test x$enable_aac = xyes; then - AC_CHECK_LIB(faad,NeAACDecInit2,,enable_aac=no) - fi - if test x$enable_aac = xyes; then - AC_MSG_CHECKING(that FAAD2 can even be used) - AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -#include <faad.h> - -int main() { - char buffer; - NeAACDecHandle decoder; - NeAACDecFrameInfo frameInfo; - NeAACDecConfigurationPtr config; - unsigned char channels; - long sampleRate; - long bufferlen = 0; - - decoder = NeAACDecOpen(); - config = NeAACDecGetCurrentConfiguration(decoder); - config->outputFormat = FAAD_FMT_16BIT; - NeAACDecSetConfiguration(decoder,config); - NeAACDecInit(decoder,&buffer,bufferlen,&sampleRate,&channels); - NeAACDecInit2(decoder,&buffer,bufferlen,&sampleRate,&channels); - NeAACDecDecode(decoder,&frameInfo,&buffer,bufferlen); - NeAACDecClose(decoder); - - return 0; -} -])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no]) - fi - if test x$enable_aac = xyes; then - AC_DEFINE(HAVE_FAAD,1,[Define to use FAAD2 for AAC decoding]) - else - AC_MSG_WARN([faad2 lib needed for MP4/AAC support -- disabling MP4/AAC support]) - fi - CFLAGS=$oldcflags - LIBS=$oldlibs - CPPFLAGS=$oldcppflags -fi - -if test x$enable_aac = xno; then - FAAD_LIBS="" - FAAD_CFLAGS="" -fi - -AC_SUBST(FAAD_CFLAGS) -AC_SUBST(FAAD_LIBS) - -]) diff --git a/m4/mpd_auto.m4 b/m4/mpd_auto.m4 index ff922fadc..56957c854 100644 --- a/m4/mpd_auto.m4 +++ b/m4/mpd_auto.m4 @@ -1,23 +1,18 @@ +dnl Parameters: varname1, description AC_DEFUN([MPD_AUTO_ENABLED], [ - var="enable_$1" - feature="$2" - - if eval "test x`echo '$'$var` = xauto"; then - AC_MSG_NOTICE([auto-detected $feature]) - eval "$var=yes" + if test x$[]enable_$1 = xauto; then + AC_MSG_NOTICE([auto-detected $2]) + enable_$1=yes fi ]) +dnl Parameters: varname1, description, errmsg AC_DEFUN([MPD_AUTO_DISABLED], [ - var="enable_$1" - feature="$2" - msg="$3" - - if eval "test x`echo '$'$var` = xauto"; then - AC_MSG_WARN([$msg -- disabling $feature]) - eval "$var=no" - elif eval "test x`echo '$'$var` = xyes"; then - AC_MSG_ERROR([$feature: $msg]) + if test x$[]enable_$1 = xauto; then + AC_MSG_WARN([$3 -- disabling $2]) + enable_$1=no + elif test x$[]enable_$1 = xyes; then + AC_MSG_ERROR([$2: $3]) fi ]) @@ -25,59 +20,59 @@ dnl Check whether a prerequisite for a feature was found. This is dnl very similar to MPD_AUTO_RESULT, but does not finalize the dnl detection; it assumes that more checks will follow. AC_DEFUN([MPD_AUTO_PRE], [ - name="$1" - var="enable_$1" - found="found_$name" - feature="$2" - msg="$3" - - if eval "test x`echo '$'$var` != xno" && eval "test x`echo '$'$found` = xno"; then - MPD_AUTO_DISABLED([$name], [$feature], [$msg]) + if test x$[]enable_$1 != xno && test x$[]found_$1 = xno; then + MPD_AUTO_DISABLED([$1], [$2], [$3]) fi ]) +dnl Evaluate a check's result. Abort if the feature was requested +dnl explicitly but is unavailable. +dnl +dnl Parameters: varname1, description, errmsg AC_DEFUN([MPD_AUTO_RESULT], [ - name="$1" - var="enable_$1" - found="found_$name" - feature="$2" - msg="$3" - - if eval "test x`echo '$'$var` = xno"; then - eval "$found=no" + if test x$[]enable_$1 = xno; then + found_$1=no fi - if eval "test x`echo '$'$found` = xyes"; then - MPD_AUTO_ENABLED([$name], [$feature]) + if test x$[]found_$1 = xyes; then + MPD_AUTO_ENABLED([$1], [$2]) else - MPD_AUTO_DISABLED([$name], [$feature], [$msg]) + MPD_AUTO_DISABLED([$1], [$2], [$3]) fi ]) -AC_DEFUN([MPD_AUTO_PKG], [ - if eval "test x`echo '$'enable_$1` != xno"; then - PKG_CHECK_MODULES([$2], [$3], - [eval "found_$1=yes"], - [eval "found_$1=no"]) +dnl Invoke a check if its configuration is "yes" or "auto" and call +dnl MPD_AUTO_RESULT. +dnl +dnl Parameters: varname1, description, errmsg, check +AC_DEFUN([MPD_AUTO], [ + if test x$[]enable_$1 != xno; then + $4 fi + MPD_AUTO_RESULT([$1], [$2], [$3]) +]) - MPD_AUTO_RESULT([$1], [$4], [$5]) +dnl Wrapper for MPD_AUTO and PKG_CHECK_MODULES. +dnl +dnl Parameters: varname1, varname2, pkgname, description, errmsg +AC_DEFUN([MPD_AUTO_PKG], [ + MPD_AUTO([$1], [$4], [$5], + [PKG_CHECK_MODULES([$2], [$3], + [found_$1=yes], + [found_$1=no])]) ]) dnl Check with pkg-config first, fall back to AC_CHECK_LIB. dnl dnl Parameters: varname1, varname2, pkgname, libname, symname, libs, cflags, description, errmsg AC_DEFUN([MPD_AUTO_PKG_LIB], [ - if eval "test x`echo '$'enable_$1` != xno"; then - PKG_CHECK_MODULES([$2], [$3], - [eval "found_$1=yes"], + MPD_AUTO([$1], [$8], [$9], + [PKG_CHECK_MODULES([$2], [$3], + [found_$1=yes], AC_CHECK_LIB($4, $5, - [eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"], - [eval "found_$1=no"], - [$6])) - fi - - MPD_AUTO_RESULT([$1], [$8], [$9]) + [found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'], + [found_$1=no], + [$6]))]) ]) dnl Wrapper for AC_CHECK_LIB. @@ -87,12 +82,81 @@ AC_DEFUN([MPD_AUTO_LIB], [ AC_SUBST([$2_LIBS], []) AC_SUBST([$2_CFLAGS], []) - if eval "test x`echo '$'enable_$1` != xno"; then - AC_CHECK_LIB($3, $4, - [eval "found_$1=yes $2_LIBS='$5' $2_CFLAGS='$6'"], - [eval "found_$1=no"], - [$5]) - fi + MPD_AUTO([$1], [$7], [$8], + [AC_CHECK_LIB($3, $4, + [found_$1=yes $2_LIBS='$5' $2_CFLAGS='$6'], + [found_$1=no], + [$5])]) +]) + +dnl Convert the given string into a string for the "default value" in +dnl the help text. If the string is a literal, then it is returned +dnl as-is; if it contains a variable reference, just "auto" is +dnl emitted. +dnl +dnl Parameters: varname1 +AC_DEFUN([MPD_FORMAT_DEFAULT], + [ifelse([$1], [], [auto], + index([$1], [$]), [-1], [$1], + [auto])]) + +dnl Wrapper for AC_ARG_ENABLE, AC_DEFINE and AM_CONDITIONAL +dnl +dnl Parameters: varname1, varname2, description, default, check +AC_DEFUN([MPD_ARG_ENABLE], [ + AC_ARG_ENABLE(translit([$1], [_], [-]), + AS_HELP_STRING([--enable-]translit([$1], [_], [-]), + [enable $3 (default: ]MPD_FORMAT_DEFAULT([$4])[)]),, + [enable_$1=]ifelse([$4], [], [auto], [$4])) + + $5 - MPD_AUTO_RESULT([$1], [$7], [$8]) + MPD_DEFINE_CONDITIONAL(enable_$1, ENABLE_$2, [$3]) +]) + +dnl Wrapper for MPD_ARG_ENABLE and MPD_AUTO +dnl +dnl Parameters: varname1, varname2, description, errmsg, default, check +AC_DEFUN([MPD_ENABLE_AUTO], [ + MPD_ARG_ENABLE([$1], [$2], [$3], [$5], [ + MPD_AUTO([$1], [$3], [$4], [$6]) + ]) +]) + +dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_PKG +dnl +dnl Parameters: varname1, varname2, pkg, description, errmsg, default, pre +AC_DEFUN([MPD_ENABLE_AUTO_PKG], [ + MPD_ARG_ENABLE([$1], [$2], [$4], [$6], [ + $7 + MPD_AUTO_PKG($1, $2, $3, $4, $5) + ]) +]) + +dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_PKG_LIB +dnl +dnl Parameters: varname1, varname2, pkg, libname, symname, libs, cflags, description, errmsg, default, pre +AC_DEFUN([MPD_ENABLE_AUTO_PKG_LIB], [ + MPD_ARG_ENABLE([$1], [$2], [$8], [$10], [ + $11 + MPD_AUTO_PKG_LIB($1, $2, $3, $4, $5, $6, $7, $8, $9) + ]) +]) + +dnl Wrapper for AC_ARG_ENABLE and MPD_AUTO_LIB +dnl +dnl Parameters: varname1, varname2, libname, symname, libs, cflags, description, errmsg, default, pre +AC_DEFUN([MPD_ENABLE_AUTO_LIB], [ + MPD_ARG_ENABLE([$1], [$2], [$7], [$9], [ + $10 + MPD_AUTO_LIB($1, $2, $3, $4, $5, $6, $7, $8) + ]) +]) + +dnl Wrapper for MPD_ENABLE_AUTO_PKG and MPD_DEPENDS +dnl +dnl Parameters: varname1, varname2, pkg, description, errmsg, default, dep_variable, dep_errmsg +AC_DEFUN([MPD_ENABLE_AUTO_PKG_DEPENDS], [ + MPD_ENABLE_AUTO_PKG([$1], [$2], [$3], [$4], [$5], [$6], + [MPD_DEPENDS([enable_$1], [$7], [$8])]) ]) diff --git a/m4/mpd_define_conditional.m4 b/m4/mpd_define_conditional.m4 new file mode 100644 index 000000000..4bc849984 --- /dev/null +++ b/m4/mpd_define_conditional.m4 @@ -0,0 +1,8 @@ +dnl Wrapper for AC_DEFINE and AM_CONDITIONAL +dnl +dnl Parameters: varname1, varname2, description +AC_DEFUN([MPD_DEFINE_CONDITIONAL], [dnl + AM_CONDITIONAL($2, test x$[]$1 = xyes) + if test x$[]$1 = xyes; then + AC_DEFINE($2, 1, [Define to enable $3]) + fi]) diff --git a/m4/mpd_func.m4 b/m4/mpd_func.m4 index 5f2bf8f3d..757cd3b6f 100644 --- a/m4/mpd_func.m4 +++ b/m4/mpd_func.m4 @@ -6,7 +6,7 @@ AC_DEFUN([MPD_OPTIONAL_FUNC], [ AC_ARG_ENABLE([$1], AS_HELP_STRING([--enable-$1], [use the function "$1" (default: auto)]), - [test xenable_$1 = xyes && AC_DEFINE([$3], 1, [Define to use $1])], + [test x$[]enable_$1 = xyes && AC_DEFINE([$3], 1, [Define to use $1])], [AC_CHECK_FUNC([$2], [AC_DEFINE([$3], 1, [Define to use $1])],)]) ]) diff --git a/m4/mpd_with_flags.m4 b/m4/mpd_with_flags.m4 new file mode 100644 index 000000000..4f82b7973 --- /dev/null +++ b/m4/mpd_with_flags.m4 @@ -0,0 +1,23 @@ +dnl Run code with the specified CFLAGS/CXXFLAGS and LIBS appended. +dnl Restores the old values afterwards. +dnl +dnl Parameters: cflags, libs, code +AC_DEFUN([MPD_WITH_FLAGS], [ + ac_save_CFLAGS="$[]CFLAGS" + ac_save_CXXFLAGS="$[]CXXFLAGS" + ac_save_LIBS="$[]LIBS" + CFLAGS="$[]CFLAGS $1" + CXXFLAGS="$[]CXXFLAGS $1" + LIBS="$[]LIBS $2" + $3 + CFLAGS="$[]ac_save_CFLAGS" + CXXFLAGS="$[]ac_save_CXXFLAGS" + LIBS="$[]ac_save_LIBS" +]) + +dnl Run code with the specified library's CFLAGS/CXXFLAGS and LIBS +dnl appended. Restores the old values afterwards. +dnl +dnl Parameters: libname, code +AC_DEFUN([MPD_WITH_LIBRARY], + [MPD_WITH_FLAGS([$[]$1_CFLAGS], [$[]$1_LIBS], [$2])]) diff --git a/m4/pretty_print.m4 b/m4/pretty_print.m4 index 687dceefe..75df7228e 100644 --- a/m4/pretty_print.m4 +++ b/m4/pretty_print.m4 @@ -1,16 +1,6 @@ AC_DEFUN([results], [ - dnl This is a hack to allow "with" names, otherwise "enable". - num=`expr $1 : 'with'` - if test "$num" != "0"; then - var="`echo '$'$1`" - else - var="`echo '$'enable_$1`" - fi - printf '(' - if eval "test x$var = xyes"; then - printf '+' - elif test -n "$3" && eval "test x$var = x$3"; then + if test x$[]enable_$1 = xyes; then printf '+' else printf '-' 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/systemd/mpd.service.in b/systemd/mpd.service.in index de91eb4d5..6dce7d93d 100644 --- a/systemd/mpd.service.in +++ b/systemd/mpd.service.in @@ -1,5 +1,6 @@ [Unit] Description=Music Player Daemon +Documentation=man:mpd(1) man:mpd.conf(5) After=network.target sound.target [Service] @@ -9,5 +10,9 @@ ExecStart=@prefix@/bin/mpd --no-daemon LimitRTPRIO=50 LimitRTTIME=-1 +# disallow writing to /usr, /bin, /sbin, ... +ProtectSystem=yes + [Install] WantedBy=multi-user.target +Also=mpd.socket diff --git a/test/.gitignore b/test/.gitignore index 132ce5fcf..a411aa32e 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1 +1,2 @@ /run_neighbor_explorer +/ReadApeTags diff --git a/test/DivideStringTest.hxx b/test/DivideStringTest.hxx new file mode 100644 index 000000000..f6c01bdde --- /dev/null +++ b/test/DivideStringTest.hxx @@ -0,0 +1,54 @@ +/* + * Unit tests for src/util/ + */ + +#include "check.h" +#include "util/DivideString.hxx" + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <string.h> + +class DivideStringTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(DivideStringTest); + CPPUNIT_TEST(TestBasic); + CPPUNIT_TEST(TestEmpty); + CPPUNIT_TEST(TestFail); + CPPUNIT_TEST(TestStrip); + CPPUNIT_TEST_SUITE_END(); + +public: + void TestBasic() { + constexpr char input[] = "foo.bar"; + const DivideString ds(input, '.'); + CPPUNIT_ASSERT(ds.IsDefined()); + CPPUNIT_ASSERT(!ds.IsEmpty()); + CPPUNIT_ASSERT_EQUAL(0, strcmp(ds.GetFirst(), "foo")); + CPPUNIT_ASSERT_EQUAL(input + 4, ds.GetSecond()); + } + + void TestEmpty() { + constexpr char input[] = ".bar"; + const DivideString ds(input, '.'); + CPPUNIT_ASSERT(ds.IsDefined()); + CPPUNIT_ASSERT(ds.IsEmpty()); + CPPUNIT_ASSERT_EQUAL(0, strcmp(ds.GetFirst(), "")); + CPPUNIT_ASSERT_EQUAL(input + 1, ds.GetSecond()); + } + + void TestFail() { + constexpr char input[] = "foo!bar"; + const DivideString ds(input, '.'); + CPPUNIT_ASSERT(!ds.IsDefined()); + } + + void TestStrip() { + constexpr char input[] = " foo\t.\nbar\r"; + const DivideString ds(input, '.', true); + CPPUNIT_ASSERT(ds.IsDefined()); + CPPUNIT_ASSERT(!ds.IsEmpty()); + CPPUNIT_ASSERT_EQUAL(0, strcmp(ds.GetFirst(), "foo")); + CPPUNIT_ASSERT_EQUAL(input + 7, ds.GetSecond()); + } +}; diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx index 07f342319..4f00006c7 100644 --- a/test/DumpDatabase.cxx +++ b/test/DumpDatabase.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,16 +27,13 @@ #include "db/LightSong.hxx" #include "db/PlaylistVector.hxx" #include "config/ConfigGlobal.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" +#include "config/Block.hxx" #include "tag/TagConfig.hxx" #include "fs/Path.hxx" #include "event/Loop.hxx" #include "util/Error.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <iostream> using std::cout; using std::cerr; @@ -44,7 +41,7 @@ using std::endl; #include <stdlib.h> -#ifdef HAVE_LIBUPNP +#ifdef ENABLE_UPNP #include "input/InputStream.hxx" size_t InputStream::LockRead(void *, size_t, Error &) @@ -107,14 +104,6 @@ main(int argc, char **argv) return EXIT_FAILURE; } - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(nullptr); -#endif -#endif - /* initialize MPD */ config_global_init(); @@ -132,13 +121,13 @@ main(int argc, char **argv) /* do it */ - const struct config_param *path = config_get_param(CONF_DB_FILE); - config_param param("database", path != nullptr ? path->line : -1); + const auto *path = config_get_param(ConfigOption::DB_FILE); + ConfigBlock block(path != nullptr ? path->line : -1); if (path != nullptr) - param.AddBlockParam("path", path->value.c_str(), path->line); + block.AddBlockParam("path", path->value.c_str(), path->line); Database *db = plugin->create(event_loop, database_listener, - param, error); + block, error); if (db == nullptr) { cerr << error.GetMessage() << endl; diff --git a/test/FakeDecoderAPI.cxx b/test/FakeDecoderAPI.cxx index 3c7453a4f..49f7a7a5e 100644 --- a/test/FakeDecoderAPI.cxx +++ b/test/FakeDecoderAPI.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -145,8 +145,13 @@ decoder_data(gcc_unused Decoder &decoder, DecoderCommand decoder_tag(gcc_unused Decoder &decoder, gcc_unused InputStream *is, - gcc_unused Tag &&tag) + Tag &&tag) { + fprintf(stderr, "TAG: duration=%f\n", tag.duration.ToDoubleS()); + + for (const auto &i : tag) + fprintf(stderr, " %s=%s\n", tag_item_names[i.type], i.value); + return DecoderCommand::NONE; } diff --git a/test/FakeDecoderAPI.hxx b/test/FakeDecoderAPI.hxx index 6f1933977..20bdee799 100644 --- a/test/FakeDecoderAPI.hxx +++ b/test/FakeDecoderAPI.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/FakeReplayGainConfig.cxx b/test/FakeReplayGainConfig.cxx index 0cb282050..a95f3e888 100644 --- a/test/FakeReplayGainConfig.cxx +++ b/test/FakeReplayGainConfig.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/ReadApeTags.cxx b/test/ReadApeTags.cxx new file mode 100644 index 000000000..a0a0e652e --- /dev/null +++ b/test/ReadApeTags.cxx @@ -0,0 +1,64 @@ +/* + * Copyright (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 "tag/ApeLoader.hxx" +#include "fs/Path.hxx" +#include "util/StringView.hxx" + +#include <stdlib.h> +#include <stdio.h> + +#ifdef HAVE_LOCALE_H +#include <locale.h> +#endif + +static bool +MyApeTagCallback(gcc_unused unsigned long flags, + const char *key, StringView value) +{ + if ((flags & (0x3 << 1)) == 0) + // UTF-8 + printf("\"%s\"=\"%.*s\"\n", key, (int)value.size, value.data); + else + printf("\"%s\"=0x%lx\n", key, flags); + return true; +} + +int +main(int argc, char **argv) +{ +#ifdef HAVE_LOCALE_H + /* initialize locale */ + setlocale(LC_CTYPE,""); +#endif + + if (argc != 2) { + fprintf(stderr, "Usage: ReadApeTags FILE\n"); + return EXIT_FAILURE; + } + + const Path path = Path::FromFS(argv[1]); + if (!tag_ape_scan(path, MyApeTagCallback)) { + fprintf(stderr, "error\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/test/ScopeIOThread.hxx b/test/ScopeIOThread.hxx index 06d27a4b8..1eb16912f 100644 --- a/test/ScopeIOThread.hxx +++ b/test/ScopeIOThread.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/ShutdownHandler.cxx b/test/ShutdownHandler.cxx index c04834444..b55b9ff6a 100644 --- a/test/ShutdownHandler.cxx +++ b/test/ShutdownHandler.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/ShutdownHandler.hxx b/test/ShutdownHandler.hxx index 9db88a1b4..e808bbeb1 100644 --- a/test/ShutdownHandler.hxx +++ b/test/ShutdownHandler.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/SplitStringTest.hxx b/test/SplitStringTest.hxx new file mode 100644 index 000000000..87ed385ea --- /dev/null +++ b/test/SplitStringTest.hxx @@ -0,0 +1,65 @@ +/* + * Unit tests for src/util/ + */ + +#include "check.h" +#include "util/SplitString.hxx" +#include "util/Macros.hxx" + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <string.h> + +class SplitStringTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(SplitStringTest); + CPPUNIT_TEST(TestBasic); + CPPUNIT_TEST(TestStrip); + CPPUNIT_TEST(TestNoStrip); + CPPUNIT_TEST(TestEmpty); + CPPUNIT_TEST_SUITE_END(); + +public: + void TestBasic() { + constexpr char input[] = "foo.bar"; + const char *const output[] = { "foo", "bar" }; + size_t i = 0; + for (auto p : SplitString(input, '.')) { + CPPUNIT_ASSERT(i < ARRAY_SIZE(output)); + CPPUNIT_ASSERT(p == output[i]); + ++i; + } + + CPPUNIT_ASSERT_EQUAL(ARRAY_SIZE(output), i); + } + + void TestStrip() { + constexpr char input[] = " foo\t.\r\nbar\r\n2"; + const char *const output[] = { "foo", "bar\r\n2" }; + size_t i = 0; + for (auto p : SplitString(input, '.')) { + CPPUNIT_ASSERT(i < ARRAY_SIZE(output)); + CPPUNIT_ASSERT(p == output[i]); + ++i; + } + + CPPUNIT_ASSERT_EQUAL(ARRAY_SIZE(output), i); + } + + void TestNoStrip() { + constexpr char input[] = " foo\t.\r\nbar\r\n2"; + const char *const output[] = { " foo\t", "\r\nbar\r\n2" }; + size_t i = 0; + for (auto p : SplitString(input, '.', false)) { + CPPUNIT_ASSERT(i < ARRAY_SIZE(output)); + CPPUNIT_ASSERT(p == output[i]); + ++i; + } + + CPPUNIT_ASSERT_EQUAL(ARRAY_SIZE(output), i); + } + + void TestEmpty() { + CPPUNIT_ASSERT(SplitString("", '.').empty()); + } +}; diff --git a/test/TestCircularBuffer.hxx b/test/TestCircularBuffer.hxx index c808d85dc..4283be45b 100644 --- a/test/TestCircularBuffer.hxx +++ b/test/TestCircularBuffer.hxx @@ -2,17 +2,12 @@ * Unit tests for class CircularBuffer. */ -#include "config.h" +#include "check.h" #include "util/CircularBuffer.hxx" #include <cppunit/TestFixture.h> -#include <cppunit/extensions/TestFactoryRegistry.h> -#include <cppunit/ui/text/TestRunner.h> #include <cppunit/extensions/HelperMacros.h> -#include <string.h> -#include <stdlib.h> - class TestCircularBuffer : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestCircularBuffer); CPPUNIT_TEST(TestIt); diff --git a/test/TestFs.cxx b/test/TestFs.cxx new file mode 100644 index 000000000..aa840f848 --- /dev/null +++ b/test/TestFs.cxx @@ -0,0 +1,107 @@ +/* + * Unit tests for src/fs/ + */ + +#include "config.h" +#include "fs/Glob.hxx" +#include "util/Error.hxx" + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <cppunit/ui/text/TestRunner.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_CLASS_GLOB + +class TestGlob : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(TestGlob); + CPPUNIT_TEST(Basic); + CPPUNIT_TEST(Asterisk); + CPPUNIT_TEST(QuestionMark); + CPPUNIT_TEST(Wildcard); + CPPUNIT_TEST(PrefixWildcard); + CPPUNIT_TEST(SuffixWildcard); + CPPUNIT_TEST_SUITE_END(); + +public: + void Basic() { + const Glob glob("foo"); + CPPUNIT_ASSERT(glob.Check("foo")); + CPPUNIT_ASSERT(!glob.Check("fooo")); + CPPUNIT_ASSERT(!glob.Check("_foo")); + CPPUNIT_ASSERT(!glob.Check("a/foo")); + CPPUNIT_ASSERT(!glob.Check("")); + CPPUNIT_ASSERT(!glob.Check("*")); + } + + void Asterisk() { + const Glob glob("*"); + CPPUNIT_ASSERT(glob.Check("foo")); + CPPUNIT_ASSERT(glob.Check("bar")); + CPPUNIT_ASSERT(glob.Check("*")); + CPPUNIT_ASSERT(glob.Check("?")); + } + + void QuestionMark() { + const Glob glob("foo?bar"); + CPPUNIT_ASSERT(glob.Check("foo_bar")); + CPPUNIT_ASSERT(glob.Check("foo?bar")); + CPPUNIT_ASSERT(glob.Check("foo bar")); + CPPUNIT_ASSERT(!glob.Check("foobar")); + CPPUNIT_ASSERT(!glob.Check("foo__bar")); + } + + void Wildcard() { + const Glob glob("foo*bar"); + CPPUNIT_ASSERT(glob.Check("foo_bar")); + CPPUNIT_ASSERT(glob.Check("foo?bar")); + CPPUNIT_ASSERT(glob.Check("foo bar")); + CPPUNIT_ASSERT(glob.Check("foobar")); + CPPUNIT_ASSERT(glob.Check("foo__bar")); + CPPUNIT_ASSERT(!glob.Check("_foobar")); + CPPUNIT_ASSERT(!glob.Check("foobar_")); + } + + void PrefixWildcard() { + const Glob glob("*bar"); + CPPUNIT_ASSERT(glob.Check("foo_bar")); + CPPUNIT_ASSERT(glob.Check("foo?bar")); + CPPUNIT_ASSERT(glob.Check("foo bar")); + CPPUNIT_ASSERT(glob.Check("foobar")); + CPPUNIT_ASSERT(glob.Check("foo__bar")); + CPPUNIT_ASSERT(glob.Check("_foobar")); + CPPUNIT_ASSERT(glob.Check("bar")); + CPPUNIT_ASSERT(!glob.Check("foobar_")); + } + + void SuffixWildcard() { + const Glob glob("foo*"); + CPPUNIT_ASSERT(glob.Check("foo_bar")); + CPPUNIT_ASSERT(glob.Check("foo?bar")); + CPPUNIT_ASSERT(glob.Check("foo bar")); + CPPUNIT_ASSERT(glob.Check("foobar")); + CPPUNIT_ASSERT(glob.Check("foo__bar")); + CPPUNIT_ASSERT(glob.Check("foobar_")); + CPPUNIT_ASSERT(glob.Check("foo")); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestGlob); + +#endif + +int +main(gcc_unused int argc, gcc_unused char **argv) +{ +#ifdef HAVE_CLASS_GLOB + CppUnit::TextUi::TestRunner runner; + auto ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + runner.addTest(registry.makeTest()); + return runner.run() ? EXIT_SUCCESS : EXIT_FAILURE; +#else + return EXIT_SUCCESS; +#endif +} diff --git a/test/TestIcu.cxx b/test/TestIcu.cxx new file mode 100644 index 000000000..484af4f22 --- /dev/null +++ b/test/TestIcu.cxx @@ -0,0 +1,86 @@ +/* + * Unit tests for src/util/ + */ + +#include "config.h" +#include "lib/icu/Converter.hxx" +#include "util/AllocatedString.hxx" +#include "util/StringAPI.hxx" +#include "util/Error.hxx" + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <cppunit/ui/text/TestRunner.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_ICU_CONVERTER + +static const char *const invalid_utf8[] = { + "\xfc", +}; + +struct StringPair { + const char *utf8, *other; +}; + +static constexpr StringPair latin1_tests[] = { + { "foo", "foo" }, + { "\xc3\xbc", "\xfc" }, +}; + +class TestIcuConverter : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(TestIcuConverter); + CPPUNIT_TEST(TestInvalidCharset); + CPPUNIT_TEST(TestLatin1); + CPPUNIT_TEST_SUITE_END(); + +public: + void TestInvalidCharset() { + CPPUNIT_ASSERT_EQUAL((IcuConverter *)nullptr, + IcuConverter::Create("doesntexist", + IgnoreError())); + } + + void TestLatin1() { + IcuConverter *const converter = + IcuConverter::Create("iso-8859-1", IgnoreError()); + CPPUNIT_ASSERT(converter != nullptr); + + for (const auto i : invalid_utf8) { + auto f = converter->FromUTF8(i); + CPPUNIT_ASSERT_EQUAL(true, f.IsNull()); + } + + for (const auto i : latin1_tests) { + auto f = converter->FromUTF8(i.utf8); + CPPUNIT_ASSERT_EQUAL(true, StringIsEqual(f.c_str(), + i.other)); + + auto t = converter->ToUTF8(i.other); + CPPUNIT_ASSERT_EQUAL(true, StringIsEqual(t.c_str(), + i.utf8)); + } + + delete converter; + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestIcuConverter); + +#endif + +int +main(gcc_unused int argc, gcc_unused char **argv) +{ +#ifdef HAVE_ICU_CONVERTER + CppUnit::TextUi::TestRunner runner; + auto ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + runner.addTest(registry.makeTest()); + return runner.run() ? EXIT_SUCCESS : EXIT_FAILURE; +#else + return EXIT_SUCCESS; +#endif +} diff --git a/test/UriUtilTest.hxx b/test/UriUtilTest.hxx new file mode 100644 index 000000000..07f52a475 --- /dev/null +++ b/test/UriUtilTest.hxx @@ -0,0 +1,66 @@ +/* + * Unit tests for src/util/ + */ + +#include "check.h" +#include "util/UriUtil.hxx" + +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +#include <string.h> + +class UriUtilTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(UriUtilTest); + CPPUNIT_TEST(TestSuffix); + CPPUNIT_TEST(TestRemoveAuth); + CPPUNIT_TEST_SUITE_END(); + +public: + void TestSuffix() { + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo/bar")); + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo.jpg/bar")); + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg"), + "jpg")); + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo.png/bar.jpg"), + "jpg")); + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix(".jpg")); + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo/.jpg")); + + /* the first overload does not eliminate the query + string */ + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string"), + "jpg?query_string")); + + /* ... but the second one does */ + UriSuffixBuffer buffer; + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string", + buffer), + "jpg")); + + /* repeat some of the above tests with the second overload */ + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo/bar", buffer)); + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo.jpg/bar", buffer)); + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg", buffer), + "jpg")); + } + + void TestRemoveAuth() { + CPPUNIT_ASSERT_EQUAL(std::string(), + uri_remove_auth("http://www.example.com/")); + CPPUNIT_ASSERT_EQUAL(std::string("http://www.example.com/"), + uri_remove_auth("http://foo:bar@www.example.com/")); + CPPUNIT_ASSERT_EQUAL(std::string("http://www.example.com/"), + uri_remove_auth("http://foo@www.example.com/")); + CPPUNIT_ASSERT_EQUAL(std::string(), + uri_remove_auth("http://www.example.com/f:oo@bar")); + CPPUNIT_ASSERT_EQUAL(std::string("ftp://ftp.example.com/"), + uri_remove_auth("ftp://foo:bar@ftp.example.com/")); + } +}; diff --git a/test/WriteFile.cxx b/test/WriteFile.cxx new file mode 100644 index 000000000..706ca65eb --- /dev/null +++ b/test/WriteFile.cxx @@ -0,0 +1,78 @@ +/* + * Copyright (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 "fs/io/FileOutputStream.hxx" +#include "util/Error.hxx" + +#include <unistd.h> +#include <errno.h> +#include <string.h> + +static bool +Copy(OutputStream &dest, int src) +{ + Error error; + + while (true) { + uint8_t buffer[8192]; + ssize_t nbytes = read(src, buffer, sizeof(buffer)); + if (nbytes < 0) { + fprintf(stderr, "Failed to read from stdin: %s\n", + strerror(errno)); + return false; + } + + if (nbytes == 0) + return true; + + if (!dest.Write(buffer, nbytes, error)) { + fprintf(stderr, "%s\n", error.GetMessage()); + return false; + } + } +} + +int +main(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: WriteFile PATH\n"); + return EXIT_FAILURE; + } + + const Path path = Path::FromFS(argv[1]); + + Error error; + FileOutputStream fos(path, error); + if (!fos.IsDefined()) { + fprintf(stderr, "%s\n", error.GetMessage()); + return EXIT_FAILURE; + } + + if (!Copy(fos, STDIN_FILENO)) + return EXIT_FAILURE; + + if (!fos.Commit(error)) { + fprintf(stderr, "%s\n", error.GetMessage()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/test/dump_playlist.cxx b/test/dump_playlist.cxx index 0047ef427..4a21e2df5 100644 --- a/test/dump_playlist.cxx +++ b/test/dump_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 @@ -35,10 +35,6 @@ #include "thread/Cond.hxx" #include "Log.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <unistd.h> #include <stdlib.h> @@ -64,14 +60,6 @@ int main(int argc, char **argv) const Path config_path = Path::FromFS(argv[1]); uri = argv[2]; - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - /* initialize MPD */ config_global_init(); diff --git a/test/dump_rva2.cxx b/test/dump_rva2.cxx index fd46ee36c..fff0aa044 100644 --- a/test/dump_rva2.cxx +++ b/test/dump_rva2.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/dump_text_file.cxx b/test/dump_text_file.cxx index 5bfd316a5..a207b3400 100644 --- a/test/dump_text_file.cxx +++ b/test/dump_text_file.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,6 @@ #include "input/InputStream.hxx" #include "input/TextInputStream.hxx" #include "config/ConfigGlobal.hxx" -#include "stdbin.h" #include "util/Error.hxx" #include "thread/Cond.hxx" #include "Log.hxx" @@ -32,10 +31,6 @@ #include "archive/ArchiveList.hxx" #endif -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -79,14 +74,6 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - /* initialize MPD */ config_global_init(); diff --git a/test/read_conf.cxx b/test/read_conf.cxx index 42afdfb4b..fdf93a40d 100644 --- a/test/read_conf.cxx +++ b/test/read_conf.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,8 +46,8 @@ int main(int argc, char **argv) } ConfigOption option = ParseConfigOptionName(name); - const char *value = option != CONF_MAX - ? config_get_string(option, nullptr) + const char *value = option != ConfigOption::MAX + ? config_get_string(option) : nullptr; int ret; if (value != NULL) { diff --git a/test/read_mixer.cxx b/test/read_mixer.cxx index 83a1d26b4..2d68aab09 100644 --- a/test/read_mixer.cxx +++ b/test/read_mixer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,14 +24,10 @@ #include "pcm/Volume.hxx" #include "Main.hxx" #include "event/Loop.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" #include "util/Error.hxx" #include "Log.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <assert.h> #include <string.h> #include <unistd.h> @@ -53,19 +49,13 @@ int main(int argc, gcc_unused char **argv) return EXIT_FAILURE; } -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - EventLoop event_loop; Error error; Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin, *(AudioOutput *)nullptr, *(MixerListener *)nullptr, - config_param(), error); + ConfigBlock(), error); if (mixer == NULL) { LogError(error, "mixer_new() failed"); return EXIT_FAILURE; diff --git a/test/read_tags.cxx b/test/read_tags.cxx index 91ac9c674..a00e87529 100644 --- a/test/read_tags.cxx +++ b/test/read_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 @@ -32,10 +32,6 @@ #include "thread/Cond.hxx" #include "Log.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <assert.h> #include <unistd.h> #include <stdlib.h> @@ -90,12 +86,6 @@ int main(int argc, char **argv) decoder_name = argv[1]; const Path path = Path::FromFS(argv[2]); -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - const ScopeIOThread io_thread; Error error; diff --git a/test/run_avahi.cxx b/test/run_avahi.cxx index b3b20365c..5c1c77d27 100644 --- a/test/run_avahi.cxx +++ b/test/run_avahi.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/run_convert.cxx b/test/run_convert.cxx index 8b9b15cf0..01cace0c3 100644 --- a/test/run_convert.cxx +++ b/test/run_convert.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,25 +27,16 @@ #include "AudioParser.hxx" #include "AudioFormat.hxx" #include "pcm/PcmConvert.hxx" -#include "config/ConfigGlobal.hxx" #include "util/ConstBuffer.hxx" #include "util/StaticFifoBuffer.hxx" #include "util/Error.hxx" #include "Log.hxx" -#include "stdbin.h" #include <assert.h> #include <stddef.h> #include <stdlib.h> #include <unistd.h> -const char * -config_get_string(gcc_unused enum ConfigOption option, - const char *default_value) -{ - return default_value; -} - int main(int argc, char **argv) { AudioFormat in_audio_format, out_audio_format; diff --git a/test/run_decoder.cxx b/test/run_decoder.cxx index 0e9af6a1a..35d55cedf 100644 --- a/test/run_decoder.cxx +++ b/test/run_decoder.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,11 +28,6 @@ #include "AudioFormat.hxx" #include "util/Error.hxx" #include "Log.hxx" -#include "stdbin.h" - -#ifdef HAVE_GLIB -#include <glib.h> -#endif #include <assert.h> #include <unistd.h> @@ -50,12 +45,6 @@ int main(int argc, char **argv) const char *const decoder_name = argv[1]; const char *const uri = argv[2]; -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - const ScopeIOThread io_thread; Error error; diff --git a/test/run_encoder.cxx b/test/run_encoder.cxx index f16d8cb0a..307d1b73d 100644 --- a/test/run_encoder.cxx +++ b/test/run_encoder.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,29 +20,20 @@ #include "config.h" #include "encoder/EncoderList.hxx" #include "encoder/EncoderPlugin.hxx" +#include "encoder/EncoderInterface.hxx" +#include "encoder/ToOutputStream.hxx" #include "AudioFormat.hxx" #include "AudioParser.hxx" -#include "config/ConfigData.hxx" +#include "config/Block.hxx" +#include "fs/io/StdioOutputStream.hxx" #include "util/Error.hxx" #include "Log.hxx" -#include "stdbin.h" #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <unistd.h> -static void -encoder_to_stdout(Encoder &encoder) -{ - size_t length; - static char buffer[32768]; - - while ((length = encoder_read(&encoder, buffer, sizeof(buffer))) > 0) { - gcc_unused ssize_t ignored = write(1, buffer, length); - } -} - int main(int argc, char **argv) { const char *encoder_name; @@ -69,11 +60,11 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - config_param param; - param.AddBlockParam("quality", "5.0", -1); + ConfigBlock block; + block.AddBlockParam("quality", "5.0", -1); Error error; - const auto encoder = encoder_init(*plugin, param, error); + const auto encoder = encoder_init(*plugin, block, error); if (encoder == NULL) { LogError(error, "Failed to initialize encoder"); return EXIT_FAILURE; @@ -89,12 +80,17 @@ int main(int argc, char **argv) } } - if (!encoder_open(encoder, audio_format, error)) { + if (!encoder->Open(audio_format, error)) { LogError(error, "Failed to open encoder"); return EXIT_FAILURE; } - encoder_to_stdout(*encoder); + StdioOutputStream os(stdout); + + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } /* do it */ @@ -105,7 +101,10 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - encoder_to_stdout(*encoder); + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } } if (!encoder_end(encoder, error)) { @@ -113,8 +112,11 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - encoder_to_stdout(*encoder); + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } - encoder_close(encoder); - encoder_finish(encoder); + encoder->Close(); + encoder->Dispose(); } diff --git a/test/run_filter.cxx b/test/run_filter.cxx index ab99c9a1e..6499b9631 100644 --- a/test/run_filter.cxx +++ b/test/run_filter.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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 "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "fs/Path.hxx" #include "AudioParser.hxx" @@ -27,16 +27,11 @@ #include "filter/FilterInternal.hxx" #include "pcm/Volume.hxx" #include "mixer/MixerControl.hxx" -#include "stdbin.h" #include "util/Error.hxx" #include "util/ConstBuffer.hxx" #include "system/FatalError.hxx" #include "Log.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <assert.h> #include <string.h> #include <stdlib.h> @@ -54,8 +49,8 @@ mixer_set_volume(gcc_unused Mixer *mixer, static Filter * load_filter(const char *name) { - const config_param *param = - config_find_block(CONF_AUDIO_FILTER, "name", name); + const auto *param = config_find_block(ConfigBlockOption::AUDIO_FILTER, + "name", name); if (param == NULL) { fprintf(stderr, "No such configured filter: %s\n", name); return nullptr; @@ -86,14 +81,6 @@ int main(int argc, char **argv) AudioFormat audio_format(44100, SampleFormat::S16, 2); - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - /* read configuration file (mpd.conf) */ config_global_init(); diff --git a/test/run_gunzip.cxx b/test/run_gunzip.cxx index 51bdb532e..7ea70678a 100644 --- a/test/run_gunzip.cxx +++ b/test/run_gunzip.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/run_gzip.cxx b/test/run_gzip.cxx index c52b32ac7..15b769f0a 100644 --- a/test/run_gzip.cxx +++ b/test/run_gzip.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/run_inotify.cxx b/test/run_inotify.cxx index df4046356..ae4f196f8 100644 --- a/test/run_inotify.cxx +++ b/test/run_inotify.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/run_input.cxx b/test/run_input.cxx index 6864a5d64..6d51b3749 100644 --- a/test/run_input.cxx +++ b/test/run_input.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,6 @@ #include "config.h" #include "TagSave.hxx" -#include "stdbin.h" #include "tag/Tag.hxx" #include "config/ConfigGlobal.hxx" #include "input/InputStream.hxx" @@ -35,10 +34,6 @@ #include "archive/ArchiveList.hxx" #endif -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <unistd.h> #include <stdlib.h> @@ -105,14 +100,6 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - /* initialize MPD */ config_global_init(); diff --git a/test/run_neighbor_explorer.cxx b/test/run_neighbor_explorer.cxx index c79948d6e..29582fc55 100644 --- a/test/run_neighbor_explorer.cxx +++ b/test/run_neighbor_explorer.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/run_normalize.cxx b/test/run_normalize.cxx index 9a361b790..092deab59 100644 --- a/test/run_normalize.cxx +++ b/test/run_normalize.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,6 @@ #include "AudioParser.hxx" #include "AudioFormat.hxx" #include "util/Error.hxx" -#include "stdbin.h" #include <stddef.h> #include <stdio.h> diff --git a/test/run_output.cxx b/test/run_output.cxx index 345127556..5100e8b44 100644 --- a/test/run_output.cxx +++ b/test/run_output.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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 "output/Internal.hxx" #include "output/OutputPlugin.hxx" -#include "config/ConfigData.hxx" +#include "config/Param.hxx" #include "config/ConfigGlobal.hxx" #include "config/ConfigOption.hxx" #include "Idle.hxx" @@ -31,15 +31,10 @@ #include "AudioParser.hxx" #include "pcm/PcmConvert.hxx" #include "filter/FilterRegistry.hxx" -#include "PlayerControl.hxx" -#include "stdbin.h" +#include "player/Control.hxx" #include "util/Error.hxx" #include "Log.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <assert.h> #include <string.h> #include <unistd.h> @@ -65,8 +60,8 @@ PlayerControl::~PlayerControl() {} static AudioOutput * load_audio_output(EventLoop &event_loop, const char *name) { - const config_param *param = - config_find_block(CONF_AUDIO_OUTPUT, "name", name); + const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT, + "name", name); if (param == NULL) { fprintf(stderr, "No such configured audio output: %s\n", name); return nullptr; @@ -163,12 +158,6 @@ int main(int argc, char **argv) AudioFormat audio_format(44100, SampleFormat::S16, 2); -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - /* read configuration file (mpd.conf) */ config_global_init(); diff --git a/test/run_resolver.cxx b/test/run_resolver.cxx index 71cadbeec..1fe458cfc 100644 --- a/test/run_resolver.cxx +++ b/test/run_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 @@ -18,7 +18,9 @@ */ #include "config.h" -#include "system/Resolver.hxx" +#include "net/Resolver.hxx" +#include "net/ToString.hxx" +#include "net/SocketAddress.hxx" #include "util/Error.hxx" #include "Log.hxx" @@ -50,7 +52,7 @@ int main(int argc, char **argv) } for (const struct addrinfo *i = ai; i != NULL; i = i->ai_next) { - const auto s = sockaddr_to_string(i->ai_addr, i->ai_addrlen); + const auto s = ToString({i->ai_addr, i->ai_addrlen}); printf("%s\n", s.c_str()); } diff --git a/test/run_storage.cxx b/test/run_storage.cxx index 9fc6e6e76..0d99676d5 100644 --- a/test/run_storage.cxx +++ b/test/run_storage.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,10 +24,6 @@ #include "storage/FileInfo.hxx" #include "util/Error.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <memory> #include <unistd.h> @@ -60,7 +56,7 @@ Ls(Storage &storage, const char *path) const char *name; while ((name = dir->Read()) != nullptr) { - FileInfo info; + StorageFileInfo info; if (!dir->GetInfo(false, info, error)) { printf("Error on %s: %s\n", name, error.GetMessage()); error.Clear(); @@ -69,15 +65,15 @@ Ls(Storage &storage, const char *path) const char *type = "unk"; switch (info.type) { - case FileInfo::Type::OTHER: + case StorageFileInfo::Type::OTHER: type = "oth"; break; - case FileInfo::Type::REGULAR: + case StorageFileInfo::Type::REGULAR: type = "reg"; break; - case FileInfo::Type::DIRECTORY: + case StorageFileInfo::Type::DIRECTORY: type = "dir"; break; } @@ -102,14 +98,6 @@ main(int argc, char **argv) return EXIT_FAILURE; } - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - const char *const command = argv[1]; const char *const storage_uri = argv[2]; diff --git a/test/software_volume.cxx b/test/software_volume.cxx index 1e41f95fd..a0dd207a7 100644 --- a/test/software_volume.cxx +++ b/test/software_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 @@ -29,7 +29,6 @@ #include "AudioFormat.hxx" #include "util/ConstBuffer.hxx" #include "util/Error.hxx" -#include "stdbin.h" #include "Log.hxx" #include <stdio.h> diff --git a/test/test_byte_reverse.cxx b/test/test_byte_reverse.cxx index 0ab97e4d1..a57753e38 100644 --- a/test/test_byte_reverse.cxx +++ b/test/test_byte_reverse.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_mixramp.cxx b/test/test_mixramp.cxx index 954aade87..ea245f72f 100644 --- a/test/test_mixramp.cxx +++ b/test/test_mixramp.cxx @@ -3,7 +3,7 @@ */ #include "config.h" -#include "CrossFade.cxx" +#include "player/CrossFade.cxx" #include <cppunit/TestFixture.h> #include <cppunit/extensions/TestFactoryRegistry.h> diff --git a/test/test_pcm_all.hxx b/test/test_pcm_all.hxx index 7cdd8b63f..fd08ec451 100644 --- a/test/test_pcm_all.hxx +++ b/test/test_pcm_all.hxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -103,6 +103,23 @@ public: void TestMix32(); }; +class PcmInterleaveTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(PcmInterleaveTest); + CPPUNIT_TEST(TestInterleave8); + CPPUNIT_TEST(TestInterleave16); + CPPUNIT_TEST(TestInterleave24); + CPPUNIT_TEST(TestInterleave32); + CPPUNIT_TEST(TestInterleave64); + CPPUNIT_TEST_SUITE_END(); + +public: + void TestInterleave8(); + void TestInterleave16(); + void TestInterleave24(); + void TestInterleave32(); + void TestInterleave64(); +}; + class PcmExportTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(PcmExportTest); CPPUNIT_TEST(TestShift8); diff --git a/test/test_pcm_channels.cxx b/test/test_pcm_channels.cxx index 748a76351..fa6aa32da 100644 --- a/test/test_pcm_channels.cxx +++ b/test/test_pcm_channels.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_pcm_dither.cxx b/test/test_pcm_dither.cxx index 09a2b5cf9..f1a375f6c 100644 --- a/test/test_pcm_dither.cxx +++ b/test/test_pcm_dither.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_pcm_export.cxx b/test/test_pcm_export.cxx index 410e64e4d..347e2394a 100644 --- a/test/test_pcm_export.cxx +++ b/test/test_pcm_export.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_pcm_format.cxx b/test/test_pcm_format.cxx index 825a8bd84..ac9e47240 100644 --- a/test/test_pcm_format.cxx +++ b/test/test_pcm_format.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_pcm_interleave.cxx b/test/test_pcm_interleave.cxx new file mode 100644 index 000000000..e7be38813 --- /dev/null +++ b/test/test_pcm_interleave.cxx @@ -0,0 +1,126 @@ +/* + * Copyright (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 "test_pcm_all.hxx" +#include "pcm/Interleave.hxx" +#include "util/Macros.hxx" + +#include <algorithm> + +template<typename T> +static void +TestInterleaveN() +{ + static constexpr T src1[] = { 1, 4, 7 }; + static constexpr T src2[] = { 2, 5, 8 }; + static constexpr T src3[] = { 3, 6, 9 }; + static constexpr const T *src_all[] = { src1, src2, src3 }; + + static constexpr size_t n_frames = ARRAY_SIZE(src1); + static constexpr unsigned channels = ARRAY_SIZE(src_all); + + static const ConstBuffer<const void *> src((const void *const*)src_all, + channels); + + static constexpr T poison = T(0xdeadbeef); + T dest[n_frames * channels + 1]; + std::fill_n(dest, ARRAY_SIZE(dest), poison); + + PcmInterleave(dest, src, n_frames, sizeof(T)); + + CPPUNIT_ASSERT_EQUAL(T(1), dest[0]); + CPPUNIT_ASSERT_EQUAL(T(2), dest[1]); + CPPUNIT_ASSERT_EQUAL(T(3), dest[2]); + CPPUNIT_ASSERT_EQUAL(T(4), dest[3]); + CPPUNIT_ASSERT_EQUAL(T(5), dest[4]); + CPPUNIT_ASSERT_EQUAL(T(6), dest[5]); + CPPUNIT_ASSERT_EQUAL(T(7), dest[6]); + CPPUNIT_ASSERT_EQUAL(T(8), dest[7]); + CPPUNIT_ASSERT_EQUAL(T(9), dest[8]); + CPPUNIT_ASSERT_EQUAL(poison, dest[9]); +} + +void +PcmInterleaveTest::TestInterleave8() +{ + TestInterleaveN<uint8_t>(); +} + +void +PcmInterleaveTest::TestInterleave16() +{ + TestInterleaveN<uint16_t>(); +} + +void +PcmInterleaveTest::TestInterleave24() +{ + typedef uint8_t T; + static constexpr T src1[] = { 1, 2, 3, 4, 5, 6 }; + static constexpr T src2[] = { 7, 8, 9, 10, 11, 12 }; + static constexpr T src3[] = { 13, 14, 15, 16, 17, 18 }; + static constexpr const T *src_all[] = { src1, src2, src3 }; + + static constexpr size_t n_frames = ARRAY_SIZE(src1) / 3; + static constexpr unsigned channels = ARRAY_SIZE(src_all); + + static const ConstBuffer<const void *> src((const void *const*)src_all, + channels); + + static constexpr T poison = 0xff; + T dest[n_frames * channels * 3 + 1]; + std::fill_n(dest, ARRAY_SIZE(dest), poison); + + PcmInterleave(dest, src, n_frames, 3); + + CPPUNIT_ASSERT_EQUAL(T(1), dest[0]); + CPPUNIT_ASSERT_EQUAL(T(2), dest[1]); + CPPUNIT_ASSERT_EQUAL(T(3), dest[2]); + CPPUNIT_ASSERT_EQUAL(T(7), dest[3]); + CPPUNIT_ASSERT_EQUAL(T(8), dest[4]); + CPPUNIT_ASSERT_EQUAL(T(9), dest[5]); + CPPUNIT_ASSERT_EQUAL(T(13), dest[6]); + CPPUNIT_ASSERT_EQUAL(T(14), dest[7]); + CPPUNIT_ASSERT_EQUAL(T(15), dest[8]); + CPPUNIT_ASSERT_EQUAL(T(4), dest[9]); + CPPUNIT_ASSERT_EQUAL(T(5), dest[10]); + CPPUNIT_ASSERT_EQUAL(T(6), dest[11]); + CPPUNIT_ASSERT_EQUAL(T(10), dest[12]); + CPPUNIT_ASSERT_EQUAL(T(11), dest[13]); + CPPUNIT_ASSERT_EQUAL(T(12), dest[14]); + CPPUNIT_ASSERT_EQUAL(T(16), dest[15]); + CPPUNIT_ASSERT_EQUAL(T(17), dest[16]); + CPPUNIT_ASSERT_EQUAL(T(18), dest[17]); + CPPUNIT_ASSERT_EQUAL(poison, dest[18]); + + TestInterleaveN<uint16_t>(); +} + +void +PcmInterleaveTest::TestInterleave32() +{ + TestInterleaveN<uint8_t>(); +} + +void +PcmInterleaveTest::TestInterleave64() +{ + TestInterleaveN<uint64_t>(); +} diff --git a/test/test_pcm_main.cxx b/test/test_pcm_main.cxx index 0e397a15c..8d478cd05 100644 --- a/test/test_pcm_main.cxx +++ b/test/test_pcm_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 @@ -31,6 +31,7 @@ CPPUNIT_TEST_SUITE_REGISTRATION(PcmChannelsTest); CPPUNIT_TEST_SUITE_REGISTRATION(PcmVolumeTest); CPPUNIT_TEST_SUITE_REGISTRATION(PcmFormatTest); CPPUNIT_TEST_SUITE_REGISTRATION(PcmMixTest); +CPPUNIT_TEST_SUITE_REGISTRATION(PcmInterleaveTest); CPPUNIT_TEST_SUITE_REGISTRATION(PcmExportTest); int diff --git a/test/test_pcm_mix.cxx b/test/test_pcm_mix.cxx index 973b58f4d..9abe476fa 100644 --- a/test/test_pcm_mix.cxx +++ b/test/test_pcm_mix.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_pcm_pack.cxx b/test/test_pcm_pack.cxx index fff3e10f0..20732f0b6 100644 --- a/test/test_pcm_pack.cxx +++ b/test/test_pcm_pack.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (C) 2003-2015 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify diff --git a/test/test_pcm_util.hxx b/test/test_pcm_util.hxx index f1efbc666..3ac84c307 100644 --- a/test/test_pcm_util.hxx +++ b/test/test_pcm_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 @@ -34,7 +34,7 @@ struct RandomInt { "RNG result type too small"); T operator()() { - return T(random()); + return T(engine()); } }; diff --git a/test/test_pcm_volume.cxx b/test/test_pcm_volume.cxx index 2d908f6c1..55b54244c 100644 --- a/test/test_pcm_volume.cxx +++ b/test/test_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/test/test_protocol.cxx b/test/test_protocol.cxx index fb35cbc66..e683fe2cb 100644 --- a/test/test_protocol.cxx +++ b/test/test_protocol.cxx @@ -1,6 +1,6 @@ #include "config.h" #include "protocol/ArgParser.hxx" -#include "protocol/Result.hxx" +#include "client/Response.hxx" #include "Compiler.h" #include <cppunit/TestFixture.h> @@ -13,10 +13,15 @@ static enum ack last_error = ack(-1); void -command_error(gcc_unused Client &client, enum ack error, - gcc_unused const char *fmt, ...) +Response::Error(enum ack code, gcc_unused const char *msg) { - last_error = error; + last_error = code; +} + +void +Response::FormatError(enum ack code, gcc_unused const char *fmt, ...) +{ + last_error = code; } class ArgParserTest : public CppUnit::TestFixture { @@ -32,21 +37,23 @@ void ArgParserTest::TestRange() { Client &client = *(Client *)nullptr; - unsigned a, b; + Response r(client, 0); + + RangeArg range; - CPPUNIT_ASSERT(check_range(client, &a, &b, "1")); - CPPUNIT_ASSERT_EQUAL(1u, a); - CPPUNIT_ASSERT_EQUAL(2u, b); + CPPUNIT_ASSERT(ParseCommandArg(r, range, "1")); + CPPUNIT_ASSERT_EQUAL(1u, range.start); + CPPUNIT_ASSERT_EQUAL(2u, range.end); - CPPUNIT_ASSERT(check_range(client, &a, &b, "1:5")); - CPPUNIT_ASSERT_EQUAL(1u, a); - CPPUNIT_ASSERT_EQUAL(5u, b); + CPPUNIT_ASSERT(ParseCommandArg(r, range, "1:5")); + CPPUNIT_ASSERT_EQUAL(1u, range.start); + CPPUNIT_ASSERT_EQUAL(5u, range.end); - CPPUNIT_ASSERT(check_range(client, &a, &b, "1:")); - CPPUNIT_ASSERT_EQUAL(1u, a); - CPPUNIT_ASSERT(b >= 999999u); + CPPUNIT_ASSERT(ParseCommandArg(r, range, "1:")); + CPPUNIT_ASSERT_EQUAL(1u, range.start); + CPPUNIT_ASSERT(range.end >= 999999u); - CPPUNIT_ASSERT(!check_range(client, &a, &b, "-2")); + CPPUNIT_ASSERT(!ParseCommandArg(r, range, "-2")); CPPUNIT_ASSERT_EQUAL(ACK_ERROR_ARG, last_error); } diff --git a/test/test_translate_song.cxx b/test/test_translate_song.cxx index e3c1bcb79..b6be581e4 100644 --- a/test/test_translate_song.cxx +++ b/test/test_translate_song.cxx @@ -38,7 +38,7 @@ uri_supported_scheme(const char *uri) return memcmp(uri, "http://", 7) == 0; } -static const char *const music_directory = "/music"; +static constexpr auto music_directory = PATH_LITERAL("/music"); static Storage *storage; static void @@ -121,9 +121,9 @@ DatabaseDetachSong(gcc_unused const Database &db, } bool -DetachedSong::Update() +DetachedSong::LoadFile(Path path) { - if (strcmp(GetURI(), uri1) == 0) { + if (path.ToUTF8() == uri1) { SetTag(MakeTag1a()); return true; } diff --git a/test/test_util.cxx b/test/test_util.cxx index 3e79aeca0..c2d73d7d9 100644 --- a/test/test_util.cxx +++ b/test/test_util.cxx @@ -3,7 +3,9 @@ */ #include "config.h" -#include "util/UriUtil.hxx" +#include "DivideStringTest.hxx" +#include "SplitStringTest.hxx" +#include "UriUtilTest.hxx" #include "TestCircularBuffer.hxx" #include <cppunit/TestFixture.h> @@ -11,64 +13,10 @@ #include <cppunit/ui/text/TestRunner.h> #include <cppunit/extensions/HelperMacros.h> -#include <string.h> #include <stdlib.h> -class UriUtilTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(UriUtilTest); - CPPUNIT_TEST(TestSuffix); - CPPUNIT_TEST(TestRemoveAuth); - CPPUNIT_TEST_SUITE_END(); - -public: - void TestSuffix() { - CPPUNIT_ASSERT_EQUAL((const char *)nullptr, - uri_get_suffix("/foo/bar")); - CPPUNIT_ASSERT_EQUAL((const char *)nullptr, - uri_get_suffix("/foo.jpg/bar")); - CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg"), - "jpg")); - CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo.png/bar.jpg"), - "jpg")); - CPPUNIT_ASSERT_EQUAL((const char *)nullptr, - uri_get_suffix(".jpg")); - CPPUNIT_ASSERT_EQUAL((const char *)nullptr, - uri_get_suffix("/foo/.jpg")); - - /* the first overload does not eliminate the query - string */ - CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string"), - "jpg?query_string")); - - /* ... but the second one does */ - UriSuffixBuffer buffer; - CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string", - buffer), - "jpg")); - - /* repeat some of the above tests with the second overload */ - CPPUNIT_ASSERT_EQUAL((const char *)nullptr, - uri_get_suffix("/foo/bar", buffer)); - CPPUNIT_ASSERT_EQUAL((const char *)nullptr, - uri_get_suffix("/foo.jpg/bar", buffer)); - CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg", buffer), - "jpg")); - } - - void TestRemoveAuth() { - CPPUNIT_ASSERT_EQUAL(std::string(), - uri_remove_auth("http://www.example.com/")); - CPPUNIT_ASSERT_EQUAL(std::string("http://www.example.com/"), - uri_remove_auth("http://foo:bar@www.example.com/")); - CPPUNIT_ASSERT_EQUAL(std::string("http://www.example.com/"), - uri_remove_auth("http://foo@www.example.com/")); - CPPUNIT_ASSERT_EQUAL(std::string(), - uri_remove_auth("http://www.example.com/f:oo@bar")); - CPPUNIT_ASSERT_EQUAL(std::string("ftp://ftp.example.com/"), - uri_remove_auth("ftp://foo:bar@ftp.example.com/")); - } -}; - +CPPUNIT_TEST_SUITE_REGISTRATION(DivideStringTest); +CPPUNIT_TEST_SUITE_REGISTRATION(SplitStringTest); CPPUNIT_TEST_SUITE_REGISTRATION(UriUtilTest); CPPUNIT_TEST_SUITE_REGISTRATION(TestCircularBuffer); diff --git a/test/test_vorbis_encoder.cxx b/test/test_vorbis_encoder.cxx index 59b901da2..81b7b1cbe 100644 --- a/test/test_vorbis_encoder.cxx +++ b/test/test_vorbis_encoder.cxx @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2014 The Music Player Daemon Project + * Copyright (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,29 +20,21 @@ #include "config.h" #include "encoder/EncoderList.hxx" #include "encoder/EncoderPlugin.hxx" +#include "encoder/EncoderInterface.hxx" +#include "encoder/ToOutputStream.hxx" #include "AudioFormat.hxx" -#include "config/ConfigData.hxx" -#include "stdbin.h" +#include "config/Block.hxx" +#include "fs/io/StdioOutputStream.hxx" #include "tag/Tag.hxx" #include "tag/TagBuilder.hxx" #include "util/Error.hxx" +#include "Log.hxx" #include <stddef.h> #include <unistd.h> static uint8_t zero[256]; -static void -encoder_to_stdout(Encoder &encoder) -{ - size_t length; - static char buffer[32768]; - - while ((length = encoder_read(&encoder, buffer, sizeof(buffer))) > 0) { - gcc_unused ssize_t ignored = write(1, buffer, length); - } -} - int main(gcc_unused int argc, gcc_unused char **argv) { @@ -53,33 +45,45 @@ main(gcc_unused int argc, gcc_unused char **argv) const auto plugin = encoder_plugin_get("vorbis"); assert(plugin != NULL); - config_param param; - param.AddBlockParam("quality", "5.0", -1); + ConfigBlock block; + block.AddBlockParam("quality", "5.0", -1); - const auto encoder = encoder_init(*plugin, param, IgnoreError()); + const auto encoder = encoder_init(*plugin, block, IgnoreError()); assert(encoder != NULL); /* open the encoder */ AudioFormat audio_format(44100, SampleFormat::S16, 2); - success = encoder_open(encoder, audio_format, IgnoreError()); + success = encoder->Open(audio_format, IgnoreError()); assert(success); - encoder_to_stdout(*encoder); + StdioOutputStream os(stdout); + + Error error; + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } /* write a block of data */ success = encoder_write(encoder, zero, sizeof(zero), IgnoreError()); assert(success); - encoder_to_stdout(*encoder); + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } /* write a tag */ success = encoder_pre_tag(encoder, IgnoreError()); assert(success); - encoder_to_stdout(*encoder); + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } Tag tag; @@ -90,10 +94,13 @@ main(gcc_unused int argc, gcc_unused char **argv) tag_builder.Commit(tag); } - success = encoder_tag(encoder, &tag, IgnoreError()); + success = encoder_tag(encoder, tag, IgnoreError()); assert(success); - encoder_to_stdout(*encoder); + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } /* write another block of data */ @@ -105,8 +112,11 @@ main(gcc_unused int argc, gcc_unused char **argv) success = encoder_end(encoder, IgnoreError()); assert(success); - encoder_to_stdout(*encoder); + if (!EncoderToOutputStream(os, *encoder, error)) { + LogError(error); + return EXIT_FAILURE; + } - encoder_close(encoder); - encoder_finish(encoder); + encoder->Close(); + encoder->Dispose(); } diff --git a/test/visit_archive.cxx b/test/visit_archive.cxx index 1ff3ba484..1e59746bd 100644 --- a/test/visit_archive.cxx +++ b/test/visit_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 @@ -18,7 +18,6 @@ */ #include "config.h" -#include "stdbin.h" #include "tag/Tag.hxx" #include "config/ConfigGlobal.hxx" #include "ScopeIOThread.hxx" @@ -30,10 +29,6 @@ #include "fs/Path.hxx" #include "util/Error.hxx" -#ifdef HAVE_GLIB -#include <glib.h> -#endif - #include <unistd.h> #include <stdlib.h> @@ -57,14 +52,6 @@ main(int argc, char **argv) const char *plugin_name = argv[1]; const Path path = Path::FromFS(argv[2]); - /* initialize GLib */ - -#ifdef HAVE_GLIB -#if !GLIB_CHECK_VERSION(2,32,0) - g_thread_init(NULL); -#endif -#endif - /* initialize MPD */ config_global_init(); diff --git a/win32/build.py b/win32/build.py new file mode 100755 index 000000000..7766a4435 --- /dev/null +++ b/win32/build.py @@ -0,0 +1,404 @@ +#!/usr/bin/env python3 + +import os, os.path +import sys, shutil, subprocess +import urllib.request +import hashlib +import re + +configure_args = sys.argv[1:] + +host_arch = 'i686-w64-mingw32' + +if len(configure_args) > 0 and configure_args[0] == '--64': + configure_args = configure_args[1:] + host_arch = 'x86_64-w64-mingw32' + +# the path to the MPD sources +mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..')) + +# output directories +lib_path = os.path.abspath('lib') + +shared_path = lib_path +if 'MPD_SHARED_LIB' in os.environ: + shared_path = os.environ['MPD_SHARED_LIB'] +tarball_path = os.path.join(shared_path, 'download') +src_path = os.path.join(shared_path, 'src') + +arch_path = os.path.join(lib_path, host_arch) +build_path = os.path.join(arch_path, 'build') +root_path = os.path.join(arch_path, 'root') + +# redirect pkg-config to use our root directory instead of the default +# one on the build host +os.environ['PKG_CONFIG_LIBDIR'] = os.path.join(root_path, 'lib/pkgconfig') + +gcc_toolchain = '/usr' + +def select_toolchain(): + global cc, cxx, ar, nm, strip, cflags, cxxflags, cppflags, ldflags, libs + + target_arch = '' + cc = os.path.join(gcc_toolchain, 'bin', host_arch + '-gcc') + cxx = os.path.join(gcc_toolchain, 'bin', host_arch + '-g++') + ar = os.path.join(gcc_toolchain, 'bin', host_arch + '-ar') + nm = os.path.join(gcc_toolchain, 'bin', host_arch + '-nm') + strip = os.path.join(gcc_toolchain, 'bin', host_arch + '-strip') + + cflags = '-O2 -g ' + target_arch + cxxflags = '-O2 -g ' + target_arch + cppflags = '-I' + root_path + '/include' + ldflags = '-L' + root_path + '/lib' + libs = '' + +def file_md5(path): + """Calculate the MD5 checksum of a file and return it in hexadecimal notation.""" + + with open(path, 'rb') as f: + m = hashlib.md5() + while True: + data = f.read(65536) + if len(data) == 0: + # end of file + return m.hexdigest() + m.update(data) + +def download_tarball(url, md5): + """Download a tarball, verify its MD5 checksum and return the local path.""" + + global tarball_path + os.makedirs(tarball_path, exist_ok=True) + path = os.path.join(tarball_path, os.path.basename(url)) + + try: + calculated_md5 = file_md5(path) + if md5 == calculated_md5: return path + os.unlink(path) + except FileNotFoundError: + pass + + tmp_path = path + '.tmp' + + print("download", url) + urllib.request.urlretrieve(url, tmp_path) + calculated_md5 = file_md5(tmp_path) + if calculated_md5 != md5: + os.unlink(tmp_path) + raise "MD5 mismatch" + + os.rename(tmp_path, path) + return path + +class Project: + def __init__(self, url, md5, installed, name=None, version=None, + base=None): + if base is None: + basename = os.path.basename(url) + m = re.match(r'^(.+)\.(tar(\.(gz|bz2|xz|lzma))?|zip)$', basename) + if not m: raise + self.base = m.group(1) + else: + self.base = base + + if name is None or version is None: + m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?)$', self.base) + if name is None: name = m.group(1) + if version is None: version = m.group(2) + + self.name = name + self.version = version + + self.url = url + self.md5 = md5 + self.installed = installed + + def download(self): + return download_tarball(self.url, self.md5) + + def is_installed(self): + global root_path + tarball = self.download() + installed = os.path.join(root_path, self.installed) + tarball_mtime = os.path.getmtime(tarball) + try: + return os.path.getmtime(installed) >= tarball_mtime + except FileNotFoundError: + return False + + def unpack(self, out_of_tree=True): + global src_path, build_path + tarball = self.download() + if out_of_tree: + parent_path = src_path + else: + parent_path = build_path + path = os.path.join(parent_path, self.base) + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + os.makedirs(parent_path, exist_ok=True) + subprocess.check_call(['/bin/tar', 'xfC', tarball, parent_path]) + return path + + def make_build_path(self): + path = os.path.join(build_path, self.base) + try: + shutil.rmtree(path) + except FileNotFoundError: + pass + os.makedirs(path, exist_ok=True) + return path + +class AutotoolsProject(Project): + def __init__(self, url, md5, installed, configure_args=[], + autogen=False, + cppflags='', + **kwargs): + Project.__init__(self, url, md5, installed, **kwargs) + self.configure_args = configure_args + self.autogen = autogen + self.cppflags = cppflags + + def build(self): + src = self.unpack() + if self.autogen: + subprocess.check_call(['/usr/bin/aclocal'], cwd=src) + subprocess.check_call(['/usr/bin/automake', '--add-missing', '--force-missing', '--foreign'], cwd=src) + subprocess.check_call(['/usr/bin/autoconf'], cwd=src) + subprocess.check_call(['/usr/bin/libtoolize', '--force'], cwd=src) + + build = self.make_build_path() + + select_toolchain() + configure = [ + os.path.join(src, 'configure'), + 'CC=' + cc, + 'CXX=' + cxx, + 'CFLAGS=' + cflags, + 'CXXFLAGS=' + cxxflags, + 'CPPFLAGS=' + cppflags + ' ' + self.cppflags, + 'LDFLAGS=' + ldflags, + 'LIBS=' + libs, + 'AR=' + ar, + 'STRIP=' + strip, + '--host=' + host_arch, + '--prefix=' + root_path, + '--enable-silent-rules', + ] + self.configure_args + + subprocess.check_call(configure, cwd=build) + subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], cwd=build) + subprocess.check_call(['/usr/bin/make', '--quiet', 'install'], cwd=build) + +class ZlibProject(Project): + def __init__(self, url, md5, installed, + **kwargs): + Project.__init__(self, url, md5, installed, **kwargs) + + def build(self): + src = self.unpack(out_of_tree=False) + + select_toolchain() + subprocess.check_call(['/usr/bin/make', '--quiet', + '-f', 'win32/Makefile.gcc', + 'PREFIX=' + host_arch + '-', + '-j12', + 'install', + 'DESTDIR=' + root_path + '/', + 'INCLUDE_PATH=include', + 'LIBRARY_PATH=lib', + 'BINARY_PATH=bin', 'SHARED_MODE=1'], + cwd=src) + +class FfmpegProject(Project): + def __init__(self, url, md5, installed, configure_args=[], + cppflags='', + **kwargs): + Project.__init__(self, url, md5, installed, **kwargs) + self.configure_args = configure_args + self.cppflags = cppflags + + def build(self): + src = self.unpack() + build = self.make_build_path() + + select_toolchain() + configure = [ + os.path.join(src, 'configure'), + '--cc=' + cc, + '--cxx=' + cxx, + '--nm=' + nm, + '--extra-cflags=' + cflags + ' ' + cppflags + ' ' + self.cppflags, + '--extra-cxxflags=' + cxxflags + ' ' + cppflags + ' ' + self.cppflags, + '--extra-ldflags=' + ldflags, + '--extra-libs=' + libs, + '--ar=' + ar, + '--enable-cross-compile', + '--arch=x86', + '--target-os=mingw32', + '--cross-prefix=' + host_arch + '-', + '--prefix=' + root_path, + ] + self.configure_args + + subprocess.check_call(configure, cwd=build) + subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], cwd=build) + subprocess.check_call(['/usr/bin/make', '--quiet', 'install'], cwd=build) + +class BoostProject(Project): + def __init__(self, url, md5, installed, + **kwargs): + m = re.match(r'.*/boost_(\d+)_(\d+)_(\d+)\.tar\.bz2$', url) + version = "%s.%s.%s" % (m.group(1), m.group(2), m.group(3)) + Project.__init__(self, url, md5, installed, + name='boost', version=version, + **kwargs) + + def build(self): + src = self.unpack() + + # install the headers manually; don't build any library + # (because right now, we only use header-only libraries) + includedir = os.path.join(root_path, 'include') + for dirpath, dirnames, filenames in os.walk(os.path.join(src, 'boost')): + relpath = dirpath[len(src)+1:] + destdir = os.path.join(includedir, relpath) + try: + os.mkdir(destdir) + except: + pass + for name in filenames: + if name[-4:] == '.hpp': + shutil.copyfile(os.path.join(dirpath, name), + os.path.join(destdir, name)) + +# a list of third-party libraries to be used by MPD on Android +thirdparty_libs = [ + AutotoolsProject( + 'http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.xz', + '5c3a34309d8b98640827e5d0991a4015', + 'lib/libogg.a', + ['--disable-shared', '--enable-static'], + ), + + AutotoolsProject( + 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz', + '28cb28097c07a735d6af56e598e1c90f', + 'lib/libvorbis.a', + ['--disable-shared', '--enable-static'], + ), + + AutotoolsProject( + 'http://downloads.xiph.org/releases/opus/opus-1.1.tar.gz', + 'c5a8cf7c0b066759542bc4ca46817ac6', + 'lib/libopus.a', + ['--disable-shared', '--enable-static'], + ), + + AutotoolsProject( + 'http://downloads.xiph.org/releases/flac/flac-1.3.1.tar.xz', + 'b9922c9a0378c88d3e901b234f852698', + 'lib/libFLAC.a', + [ + '--disable-shared', '--enable-static', + '--disable-xmms-plugin', '--disable-cpplibs', + ], + ), + + ZlibProject( + 'http://zlib.net/zlib-1.2.8.tar.xz', + '28f1205d8dd2001f26fec1e8c2cebe37', + 'lib/libz.a', + ), + + AutotoolsProject( + 'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.1b.tar.gz', + 'e5808ad997ba32c498803822078748c3', + 'lib/libid3tag.a', + ['--disable-shared', '--enable-static'], + autogen=True, + ), + + FfmpegProject( + 'http://ffmpeg.org/releases/ffmpeg-2.6.2.tar.bz2', + 'e75d598921285d6775f20164a91936ac', + 'lib/libavcodec.a', + [ + '--disable-shared', '--enable-static', + '--enable-gpl', + '--enable-small', + '--disable-pthreads', + '--disable-programs', + '--disable-doc', + '--disable-avdevice', + '--disable-swresample', + '--disable-swscale', + '--disable-postproc', + '--disable-avfilter', + '--disable-network', + '--disable-encoders', + '--disable-protocols', + '--disable-outdevs', + '--disable-filters', + ], + ), + + AutotoolsProject( + 'http://curl.haxx.se/download/curl-7.42.1.tar.lzma', + 'f1e460020a99da039b153e4bf0cd8600', + 'lib/libcurl.a', + [ + '--disable-shared', '--enable-static', + '--disable-debug', + '--enable-http', + '--enable-ipv6', + '--disable-ftp', '--disable-file', + '--disable-ldap', '--disable-ldaps', + '--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet', + '--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp', + '--disable-gopher', + '--disable-manual', + '--disable-threaded-resolver', '--disable-verbose', '--disable-sspi', + '--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies', + '--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2', + ], + ), + + BoostProject( + 'http://netcologne.dl.sourceforge.net/project/boost/boost/1.58.0/boost_1_58_0.tar.bz2', + 'b8839650e61e9c1c0a89f371dd475546', + 'include/boost/version.hpp', + ), +] + +# build the third-party libraries +for x in thirdparty_libs: + if not x.is_installed(): + x.build() + +# configure and build MPD +select_toolchain() + +configure = [ + os.path.join(mpd_path, 'configure'), + 'CC=' + cc, + 'CXX=' + cxx, + 'CFLAGS=' + cflags, + 'CXXFLAGS=' + cxxflags, + 'CPPFLAGS=' + cppflags, + 'LDFLAGS=' + ldflags + ' -static', + 'LIBS=' + libs, + 'AR=' + ar, + 'STRIP=' + strip, + '--host=' + host_arch, + '--prefix=' + root_path, + + '--enable-silent-rules', + + '--disable-icu', + +] + configure_args + +subprocess.check_call(configure) +subprocess.check_call(['/usr/bin/make', '--quiet', '-j12']) 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 |